react-native 渲染优化点

2019-05-13  本文已影响0人  嘴角45度

使用react-native一段时间了,从它的启动逻辑,以及页面的加载、渲染、滚动、跳转等都用得得心应手了。但是在用户反馈中,经常有用户会反馈,使用我们APP一段时间后,感觉电量消耗得特别的快,用久了后,APP还会崩溃,这引起了大家的注意,于是决定深入调查并解决一下这个问题。

请教了一下公司的原生工程师,衡量一个APP性能优劣的点有哪些,总结如下:

  1. 内存优化
  1. GPU消耗(view在进行渲染时,需要占用系统的GPU资源)
  1. 电量的消耗(这个的消耗取决于CPU以及GPU的消耗,CPU和GPU使用越多,电量消耗越大)

对于以上页面的问题,咱们分解决地解决了它,第一步是解决内存优化,在些先不做过多讲解,先主要讲渲染方面的优化。

前提(掌握基础知识,明确优化目标)

  1. 写一个页面或者封装一个组件时,什么时候用PureComponent ?什么时候用Component + shouldComponentUpdate?什么时候用无状态组件?


    几种不同的组件的使用场景
  2. 数据的存储位置有哪些,哪些什么引起页面重新渲染?


    数据的存储位置
  3. 如何看优化后性能带来多在的提高?

具体查看方法参考 RN如何分别测试app在android和iOS上的渲染性能?

优化点

1. 滚动时的重复渲染

首先,使用FlatListSectionList代替ListView
有时会对滚动进行一个监听,当页面滚动到一定的位置时,页面的头部需要更改一个样式。未优化之前的代码如下:

页面滚动时,更新是否显示毛玻璃头部样式
这样存在一个很大的性能消耗,contentOffsetY <= 1时就使用setState方法让blurHeaderTransparent改成true, 此时如果blurHeaderTransparent已经是true了,完全没有必要重重新更新它为true, 同样,在else里面, 不管blurHeaderTransparent是否是false, 均重新更新它为false
解决方法如下:
image.png
多加了两个判断,在页面滚动时,会少许多的重复渲染。

2. 善用Component + shouldComponentUpdate

紧接着上面的问题,一个滚动列表FlatList有很多的row需要渲染,由于业务需求,row里面的显示会根据传入的type的不同,显示不同的样式,咱们在做的时候,想把这个判断封装在row里面,type === a 显示 style1type === b 显示style2,根据约定的前提,这种情况这个组件就只能使用PureComponent或者Compnent + shouldComponentUpdate,看了一下传入的props,发现传入的数据较多,内容层级较深,有一个使用的地方是这样的const readDate = this.props.rowData.item.readDate,如果此时使用PureComponent,很可能会出现因为数据改变了,但是页面不刷新的问题,所以这种情况使用Component + shouldComponentUpdate是更好的方式,观察这个组件中有什么是需要重新渲染的, 发现只有小红点在点击后需要重新渲染,所以优化的代码如下所示:

image.png

3. 不用重新渲染的数据放在了state下面

页面在上拉加载更多时,有分页请求数据,每次请求的数据page参数都需要+1, 这一类的数据不会在页面中渲染的,所以它也就不用放在state下面,如果在在这个页面或者这个组件中的多处会共用这个数据,可以将它直接挂载到该组件的this下面。

4. 调用组件时,传入的回调函数直接使用箭头函数

<ChildComponent
          image={imgUrl}
          title={title}
          titleFontColor={fontColor}
          iconColor={iconColor}
          handlePress={() => this.handlePress(module, id, listId)}
        />

在React的事件处理里面有说,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染,原因是父组件在rerender时,直接使用箭头函数就会创建一个全新的函数传入子组件中,这样在子组件中的diff算法计算时,就默认是有更新,导致一次多余的rerender。
解决办法:
1). 父组件中的方法使用react推荐的,在constructor中使用.bind的方式,如:

this.handlePress = this.handlePress.bind(this);

2). 在父组件中定义方法是 使用 =>的方式,如:

handlePress = (params) => {
// do something
}

4. 在调用子组件时,直接给子组件再传入一个组件

render() {
    const rightComponent = (
      <Btn
        size={'xs'}
        type={'Home'}
      />
    );
    return (
      <ListHeader
        title={'标题'}
        rightComponent={rightComponent}
      />
    );
  }

这样每次在render的时候, rightComponent都会生成一个全新的,导致子组件ListHeader在执行diff算法时,当成了props是有改变的,导致一次多余的重复渲染。修改方式如下:

render() {
    return (
      <ListHeader
        title={'标题'}
        rightComponent={this.rightComponent}
      />
    );
  }

  rightComponent = (
    <Btn
      size={'xs'}
      type={'Home'}
    />
  )

经过测试,优化后的页面,进入页面,它与未优化的变化不是很明显,但是在页面滚动以及上拉加载更多后,与未优化过的页面对比,CPU降低40%,内存的增长在合理范围内,电量的消耗明显降低。

这是目前总结的,还有没有总结到的地方,欢迎大家评论,一起将RN的渲染优化做得更好~~~

总结

渲染优化点2.jpg

参考资料

  1. React Native 性能优化总结
  2. react/react-native性能优化
  3. https://facebook.github.io/react-native/docs/performance
  4. https://react.docschina.org/docs/optimizing-performance.html?no-cache=1
上一篇下一篇

猜你喜欢

热点阅读