在原生开发中,集成友盟之后,它的 SDK 会自动帮助我们统计页面的流转情况,iOS 中它收集的是 ViewController 的类名,同样的,安卓中它收集的是 Activity 的名称。

页面统计集成正确,才能够获取正确的页面访问路径、访问深度(PV)的数据。

以 iOS 为例,那么 React Native 应用在某种程度上是单页应用,全局只有一个 ViewController,安卓上也是只有一个 Activity。那么自动的页面统计就失去了效果,幸亏友盟的 SDK 暴露了手动记录页面统计的 API,让这个统计功能重新可用。看了网上很多的所谓 React Native 下的教程,对这部分只是浅尝辄止,或者是根本都不提,所以我想我有必要分享一下我的解决经验。

最初能想到的有两种解决方法。第一种是按照 React 组件的 生命周期,类似于 - (void)viewWillAppear:(BOOL)animated;- (void)viewWillDisappear:(BOOL)animated;,在 componentWillMount()componentWillUnmount() 中加入统计的代码,但这要求所有的组件 均继承自这个 BaseComponent,且子组件在覆写这两个方法是要保证调用 super 方法,因此对现有的代码改动过大,也不符合 React 的 Composition over Inheritance 原则,所以产生了第二种的解决方法。

假设你的 App 现在已经在使用 reduxreact-native-router-fluxredux-sagas 这一套主流的框架了。根据观察,react-native-router-flux 底层的 react-navigation 在页面流转时,会先 dispatch 一个代表当前页面 disappear 的 action,再 dispatch 一个代表新页面 appear 的 action。这个反映到 react-native-router-flux 就是 ActionConst.BLURActionConst.FOCUS 的两个 action。咨询过友盟的客服,这个流程和友盟要求的页面统计方法是一致的(先退出前一个页面,再进入新页面),所以此方案可行。

首先在声明式路由的实现文件中,将 react-native-router-flux 的路由中产生的所有 action 都 dispatch 到 redux 的 store 中,大致的代码如下:

reducerCreate = (params: any) => {
  const defaultReducer = new Reducer(params);
  return (state: Object, action: Object) => {
    typeof this.props.dispatch === 'function' && this.props.dispatch(action);
    return defaultReducer(state, action);
  };
};

这样当底层的 react-navigation 进行页面的流转时,不同的 action 会被正确传到 redux 的 store 中,因为没有任何一个匹配的 reducer,所以不会对 redux 的 store 造成任何影响。

下一步就是监听这些 actions 了。我们创建两个 generator function,把它放到 redux-sagas 中,大致的代码如下:

这样当页面的流转时,友盟的 SDK 已经可以正常记录了。

最后有一个细节问题是,这个方法会导致第一个页面记录的失常。比如你在声明式路由的实现中指定了某个组件是 initial 的,这个组件会成为 App 打开进入的第一个页面,但进入这个页面的事件并不会被触发,所以需要手动补充。我是在 App 根组件 的 componentDidMount() 中手动补上了。

至此,友盟应该可以以声明式路由中各 Component 的 key 值来记录各个页面的流转信息。

如图所示,最终效果和原生开发的自动收集相差无几了:

Screen-Shot-2018-08-13-at-11.42.35-PM