美文网首页react-native
RN对性能监控的思考及工具分享

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

作者: LaxusJ | 来源:发表于2019-12-13 19:43 被阅读0次

    分享内容

    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

    相关文章

      网友评论

        本文标题:RN对性能监控的思考及工具分享

        本文链接:https://www.haomeiwen.com/subject/zmylictx.html