美文网首页
react-native 渲染优化点

react-native 渲染优化点

作者: 嘴角45度 | 来源:发表于2019-05-13 15:20 被阅读0次

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

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

    1. 内存优化
    • 内存回收(进入一个页面,然后退出这个页面,此页面加载过的资源由于不需要了,应该被内存自动回收)
    • 内存泄露
    1. GPU消耗(view在进行渲染时,需要占用系统的GPU资源)
    • 重复渲染
    • 嵌套的层级,层级越深,解析时所占用的资源相对会更多
    1. 电量的消耗(这个的消耗取决于CPU以及GPU的消耗,CPU和GPU使用越多,电量消耗越大)

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

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

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


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


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

    • CPU的消耗
    • memory的占用
    • energy 的消耗

    具体查看方法参考 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

    相关文章

      网友评论

          本文标题:react-native 渲染优化点

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