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,导致<Child />组件对<Node /><App />都产生了依赖关系。一旦脱离了这两个组件,<Child />的可用性就无法保障了,减低了<Child />的复用性。

思考

适用于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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,968评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,682评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,254评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,074评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,964评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,055评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,484评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,170评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,433评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,512评论 2 308
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,296评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,184评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,545评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,150评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,437评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,630评论 2 335

推荐阅读更多精彩内容

  • 深入JSX date:20170412笔记原文其实JSX是React.createElement(componen...
    gaoer1938阅读 8,026评论 2 35
  • 3. JSX JSX是对JavaScript语言的一个扩展语法, 用于生产React“元素”,建议在描述UI的时候...
    pixels阅读 2,794评论 0 24
  • 作为一个合格的开发者,不要只满足于编写了可以运行的代码。而要了解代码背后的工作原理;不要只满足于自己的程序...
    六个周阅读 8,406评论 1 33
  • react 基本概念解析 react 的组件声明周期 react 高阶组件,context, redux 等高级...
    南航阅读 1,040评论 0 1
  • It's a common pattern in React to wrap a component in an ...
    jplyue阅读 3,224评论 0 2