美文网首页React Native学习React Native
React Native 组件性能优化

React Native 组件性能优化

作者: yuzhiyi_宇 | 来源:发表于2018-01-20 16:37 被阅读283次

    前言

    从以往的经验和实践中,我们知道影响性能最大的因素是界面的重绘,React 背后的 Virtual DOM 就是尽可能地减少重绘。
    对于性能优化这个主题,我们往往会基于 "不信任" 的前提,即我们需要提高 React Virtual DOM 的效率。从 React 渲染过程看,如何防止不必要的渲染可能是最需要取解决的问题。针对这个问题,React 官方提供了一个便捷的方法来解决,那就是 PureRender。

    纯函数

    要理解 PureRender 中的 Pure,要从函数式编程的的基本概念 "纯函数"讲起。纯函数由三大原则构成。

    • 给定相同的输入,他总是返回相同的输出

    对于一个方法,只要我们传入的值是固定,无论做多少次调用,结果都是一样。有些方法不依赖于你传入的参数,如 Math.random(),不传任何参数到方法中,该方法依然总是会输出不同的结果,这种方法也是纯函数。还有常用的 slice 和 splice 方法,slice在参数一致的时候结果都是一致的,splice方法的执行结果会改变原来数组,对于程序来说,splice 的隐藏行为是危险的,所以 splice 不是纯函数。

    • 过程没有副作用

    在纯函数中我们不能改变外部状态。

    • 没有额外的状态依赖

    方法内的状态都只在方法的生命周期内存活,意味着不能在方法内使用共享变量,这会给方法带来不可知因素。

    纯函数也是函数式编程的基础,他完全独立于外部状态,这样就避免了因为共享外部状态而导致的 bug。
    纯函数非常方便方法级别的测试以及重构,可以让程序具有良好的扩展性及适应性。
    React 就是函数式编程,React 组件本身就是纯函数,可以表示为 UI = f(data),data 包括props 和 state,改变 data,就能改变 UI,而不是像 JQuery 一样直接操纵 UI。
    可以通过拆分组件为自组件,进而对组件做更颗粒度的控制。只是函数式编程的魅力之一,保持纯净状态,让方法或组件更加专注 (focused),体积更小 (small),更独立(independent),更具有复用性 (reustability)。

    PureRender

    pureRender 指的就是组件满足春寒书条件,即组件的渲染是被相同的 props 和 state 渲染进而得到相同的结果。

    1. PureRender 的本质

    React 生命周期中有一个 shouldComponentUpdate 方法,如果返回 true,表示需要渲染,返回 false 表示不需要渲染。

    1. 运行 PureRender

    React 新版本提供一个 PureComponent,PureComponent 内部重新实现 shouldComponentUpdate 方法,让当前传入的 props 和 state 与之前的作浅比较,,浅比较对 object 只作了引用比较,并没有做值比较,如果返回 false,那么组件就不会执行 render 方法。

    1. 优化 PureRender
    • 直接为props设置对象或数组
      每次调用 React 组件其实都会重新创建组件,就算传入的数组或对象的值没有改变,它们引用的地址也会发生改变。
    <TestComponent 
        style={{background: 'white'}}
    />
    

    我们可以把传入的数组或对象保存成一份引用。

    <TestComponent 
        style={styles.container}
    />
    const styles = StyleSheet.create({
        container: {
            background: 'white'
        },
    });
    
    • 设置 props 方法并通过事件绑定在元素上
    onPress() {
    }
    <TestComponent 
        onPress={this.onPress.bind(this)}
    />
    

    这样写,每一次渲染都会重新绑定 onPress方法, 不要让方法每一次都绑定,因此把绑定移动到构造器内。

    constructor(props) {
        super(props);
        this.onPress = this.onPress.bind(this);
    }
    
    onPress() {
    }
    
    render() {
        <TestComponent 
            onPress={this.onPress}
        />
    }
    

    Immutable

    在传递的数据的时候,可以直接使用 Immutable Data 来进一步提高组件的渲染性能。

    1. Immutable Data
      Immutable Data 就是一旦创建,就不能再更改的数据。对 Immutable 对象进行修改,添加或者删除操作,都会返回一个新的 Immutable 对象。Immutable 实现的原理是持久化的数据结构,也就是使用旧数据创建新数据,要保证就数据同时可用切不变。同时为了避免深拷贝把所有节点都复制一遍带来的性能损耗,Immtable 使用了结构共享,即如果对象树中一个节点发生变化,只修改这个节点和受他影响的父节点,其他节点进行共享。
    • Map:键值对集合,对应于Object。
    • List:有序可重复的列表,对应于Array。
    • ArraySet:无序切不可重复的列表。
    1. Immutable 的优点
    • 降低了 "可变" 带来的复杂度。
    • 节省内存。Immutable 使用结构共享尽量复用内存。没有被引用的对象会被垃圾回收。
    • 撤销/重做,复制/粘帖,甚至时间旅行这些功能做起来都变得简单。因为每次数据都是不一样的,那么只要把这些数据都放到一个数组里存储起来,想回退到哪里,就拿出对应的数据。
    • 并发安全。Immutable 数据天生不可变,但 js 是单线程运行,现在并没有什么用。
    • 拥抱函数式编程。只要输入一致,那么输出必然一致,这样开发的组件更易于调试和组装。
    1. 使用 Immutable 的缺点
      容易与原生对象混淆。
    2. Immutable.is
      为了直接比较对象的值,Immutable提供了Immutable.is 来作"值比较"
    const map1 = Immutable.Map({a: 1, b: 1})
    const map2 = Immutable.Map({a: 1, b: 1})
    map1 === map2; // => false
    Immutable.is(map1, map2); // => true
    

    Immutable.is比较的是两个对象的hashCode 或 valueOf (对于JS 对象),由于Immutable 内部使用 trie 数据结构来存储,只要两个对象 hashCode 相等,值就是一样的。
    5.Immutable 和 PureRender
    React 做性能优化最常用的就是 shouldComponentUpdate 方法,但他默认返回true,即始终会执行 render 方法,然后做 Virtual DOM比较,并得出是否需要做真实 DOM 更新,这里往往带来不必要的渲染,
    当然,我们可以在 shouldComponentUpdate 中使用深拷贝和深比较来避免无必要的 render,但是深拷贝和深比较一般都是非常昂贵的选择。
    Immutable.js 提供了简洁,高效的判断数据是否变化的方法,只需要 is 比较就能知道是否需要执行 render,而这个操作几乎零成本,所以可以极大提高性能。修改后的 shouldComponentUpdate 是这样的。

    import React, {
        Component
    } from 'react';
    import Immutable from 'immutable';
    
    class BaseComponent extends Component {
    
        shouldComponentUpdate(nextProps, nextState = {}) {
            return !Immutable.is(Immutable.fromJS(this.props), Immutable.fromJS(nextProps))
            || !Immutable.is(Immutable.fromJS(this.state), Immutable.fromJS(nextState));
        }
    }
    
    export default BaseComponent;
    

    而在自定义组件你只需要继承BaseComponent,就可以拦截不必要的渲染。

    class App extends BaseComponent {
    
    }
    
    1. key
      写动态组件的时候,需要给动态子组件添加 key props,否则会报一个警告。Key 要保持惟一,稳定。

    相关文章

      网友评论

        本文标题:React Native 组件性能优化

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