RN对性能监控的思考及工具分享

分享内容

  1. 全局属性Context
  2. 性能监控
  3. debug工具

一.全局属性Context

1.概念

当你不想在组件树中通过逐层传递props或者state的方式来传递数据时,可以使用Context来实现跨层级的组件数据传递。


image.png

上图,使用props或者state传递数据,数据自顶下流。


image.png

使用Context,可以跨越组件进行数据传递。

使用Context

如果要Context发挥作用,需要用到两种组件,一个是Context生产者(Provider),通常是一个父节点,另外是一个Context的消费者(Consumer),通常是一个或者多个子节点。所以Context的使用基于生产者消费者模式。

对于父组件,也就是Context生产者,需要通过一个静态属性childContextTypes声明提供给子组件的Context对象的属性,并实现一个实例getChildContext方法,返回一个代表Context的纯对象 (plain object) 。

export default class extends Component {

  // 声明Context对象属性
  static childContextTypes = {
    navigation: PropTypes.object
  };

  constructor(props) {
    super(props);
  }

  // 返回Context对象,方法名是约定好的
  getChildContext() {
    return {
      navigation: this.props.navigation
    };
  }
}

而对于Context的消费者,需要在静态属性中声明要使用的属性,子组件需要通过一个静态属性contextTypes声明后,才能访问父组件Context对象的属性,否则,即使属性名没写错,拿到的对象也是undefined。

export default class InfoView extends PureComponent {

  // 声明需要使用的Context属性
  static contextTypes = {
    navigation: PropTypes.object
  };

  onPressEdit = () =>{
    this.context.navigation.push('Choice');
  };
}

2.几个可以直接获取Context的地方

实际上,除了实例的context属性(this.context),React组件还有很多个地方可以直接访问父组件提供的Context。比如构造方法:

  • constructor(props, context)

比如生命周期:

  • componentWillReceiveProps(nextProps, nextContext)
  • shouldComponentUpdate(nextProps, nextState, nextContext)
  • componetWillUpdate(nextProps, nextState, nextContext)

对于面向函数的无状态组件,可以通过函数的参数直接访问组件的Context。
const StatelessComponent = (props, context) => (
......
)

2.关注Context的可控性和影响范围

React App的组件是树状结构,一层一层延伸,父子组件是一对多的线性依赖。随意的使用Context其实会破坏这种依赖关系,导致组件之间一些不必要的额外依赖,降低组件的复用性,进而可能会影响到App的可维护性。

image.png

通过上图可以看到,原本线性依赖的组件树,由于子组件使用了父组件的Context,导致组件对都产生了依赖关系。一旦脱离了这两个组件,的可用性就无法保障了,减低了的复用性。

思考

适用于Context的应用场景

参考

https://juejin.im/post/5a90e0545188257a63112977#heading-6

二.性能监控

背景

现在大规模采用RN开发,但尚缺乏自动化、工具级性能采集监控,导致以下问题


image.png

目标

针对开发和项目过程痛点,我们期望的目标如下:

  • 项目性能评估客观可量化
  • 自动化侦测性能缺陷
  • 问题定位辅助决策

方案拆解思路:首先是找到针对React Native的性能相关性数据,也就是当性能出现下滑时,可以通过哪些维度的数据表现出来;然后有了数据样本需要进行记录;之后时对瞬时和一段周期内的样本进行自动化分析;最后时提供结果反馈。


image.png

解决方案

首先是性能相关性数据的实时采样模块,包括MRT(消息响应及时性)、GCP(绘图指令生效推迟)、逻辑同步帧率等。
其次是对样本的记录,记录模块目前支持两种记录模式,一种是存储在手机本地,一种是提供外放协议可以把数据投递到外部对接系统。
之后是实时分析,基于记录的各个维度数据进行缺陷侦测并生成预警。通过输出模块输出到开发者日志和可视化报表,这里我们后期有计划自动生成对应项目的性能bug对接到QA系统。

image.png

如何采集相关性数据、分析规则与调优策略

在行业缺乏相关方案的背景下,最难地方在于寻找React Native应用的性能相关性数据都是什么,在哪里,围绕RN的实现原理我们挖掘到了这些维度:

  • MRT(消息相响应及时性)
  • GCP(绘图指令延迟)
  • 无SCU优化、冗余render调用侦测
  • 绘图帧率与逻辑同步帧率
  • 关键线程CPU负载
  • 内存用量
  • 流量消耗
image.png

实践——针对SCU做的性能监控

支持全组件监控插件

利用高阶组件在声明周期hock方法上埋点

  • 计算渲染时间
  • 记录组件渲染次数


    image.png

mobx组件渲染时间监控

  1. mobx源码
componentDidMount: function componentDidMount() {
    if (isDevtoolsEnabled) {
      reportRendering(this);
    }
  },
componentDidUpdate: function componentDidUpdate() {
    if (isDevtoolsEnabled) {
      reportRendering(this);
    }
  },
function reportRendering(component) {
  var node = findDOMNode$1(component);
  if (node && componentByNodeRegistry) componentByNodeRegistry.set(node, component);
  renderReporter.emit({
    event: "render",
    renderTime: component.__$mobRenderEnd - component.__$mobRenderStart,
    totalTime: Date.now() - component.__$mobRenderStart,
    component: component,
    node: node
  });
}
  1. 使用

安装依赖

npm install mobx-devtools --save-dev

在page/index.js中统一监听时间

import { renderReporter } from 'mobx-react';
export default class extends Component {
  constructor(props) {
    ...
    renderReporter.on((report) => {
      console.warn(report.component.constructor.name + '  renderTime' + report.renderTime + '  totalTime' + report.totalTime);
    });
  }
}
image.png

参考

https://www.jianshu.com/p/943c3e7a8cd1

三.debug工具

image.png

1.mobx-监控工具

能力

  • 实时观测mobx数据,不用debug就可以看到数据结构
  • 监测mobx数据变化
  • 监测mobx数据处理时间

依赖

npm install mobx-remotedev --save-dev
npm install remotedev-server --save-dev

使用

在所有state地方加上@remotedev(配置)

// detail/state.js

@remotedev({ remote: true, onlyActions: true, global: true, hostname: 'localhost', port: 8000,  })
export default class State {
  // page
  @observable result = {};

  @observable pageState = PAGE_STATE.INIT;

  @action setResult(data) {
    this.result = data;
  }

  @action setPageState(state) {
    this.pageState = state;
  }
}

启动remotedev服务,配置以下命令手动开启

"scripts": {
    "remotedev": "remotedev --hostname=localhost --port=8000"
  },

问题

  1. mobx.getDebugName is not a function
    解决方案
    需要把本地node_module/mobx-remotedev/lib目录下的
    spy.js和utils.js里面的mobx.getDebugName(obj)改个名字
    最终解决方案
    升级mobx版本
"mobx": "^5.15.0",
"mobx-react": "^5.4.4",
  1. socket hang up
    socket服务没连上
    检查是否执行了remotedev
    ios调试模式不支持localhost,需要把hostname设置成电脑ip地址

2. 彩蛋debug in webstrom

image.png

设置

chrome静默debug能力

-headless --disable-gpu
image.png

参考

https://github.com/zalmoxisus/mobx-remotedev/issues/9
https://github.com/zalmoxisus/remotedev-server

你可能感兴趣的:(RN对性能监控的思考及工具分享)