最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • immutable data 是如何优化 React 性能的

    正文概述 掘金(yuanzili)   2021-06-11   364

    今天我们解决以下几个问题,什么是immutable datamutable data带来了哪些问题,immutable data优化了哪些性能?

    mutable data 数据的可变性

    数据的可变性用一段代码就可以描述清楚

    const a = [{ todo: 'Learn js'}, {todo: 'Learn react'}];
    const b = [...a];
    b[1].todo = 'Learn vue';
    console.log(a[1].todo); //Learn vue
    

    其实可以一眼看出这是浅copy导致的问题。内层的对象指向堆内存地址相同,所以修改b数组中的对象,a数组也会发生变化。平时大伙在项目中操作比较复杂的数据结构时,都习惯性deepCopy,否则就会出现一些不易察觉的bug。

    immutable数据优化了哪些性能

    首先我们要看一下React 是如何渲染的。

    React 渲染机制解析

    graph LR
    setState或者props改变 --> shouldComponentUpdate --true --> 递归render 
    递归render --> componentDidUpdate 
    

    在React中,render函数返回虚拟dom树,并经过Diff算法计算出与上次虚拟dom的区别,针对差异的部分做更新,渲染出真实dom。

    递归render的过程是性能消耗的大头,如果shouldComponentUpdate返回false,更新的过程就会被打住,所以我们要好好的利用这个shouldComponentUpdate。

    shouldComponentUpdate

    这是一个组件的子树。每个节点中,SCU 代表 shouldComponentUpdate 返回的值,而 vDOMEq 代表返回的 React 元素是否相同。最后,圆圈的颜色代表了该组件是否需要被调停,红色代表shouldComponentUpdate返回true,进行render,绿色代表返回false,不进行render。

    immutable data 是如何优化 React 性能的

    c1是红色节点,shouldComponentUpdate返回 true,进入diff算法比对新旧VDom树,如果新旧VDom树中节点类型不同,则全部替换,包括下面子组件,图中展示的是节点类型相同的情况,则递归子组件

    //什么是节点类型不同
    <A>
      <C/>
    </A>
    // A与B是不同节点类型
    <B>
      <C/>
    </B>
    React会直接删掉A节点(包括它所有的子节点),然后新建一个B节点插入。
    

    节点 C2 的 shouldComponentUpdate 返回了 false,React 因而不会调用 C2 的 render,也因此 C4 和 C5 的 shouldComponentUpdate 不会被调用到。

    C3,shouldComponentUpdate 返回了 true,所以 React 需要继续向下查询子节点。这里 C6 的 shouldComponentUpdate 返回了 true,同时由于渲染的元素与之前的不同使得 React 更新了该 DOM。

    最后一个有趣的例子是 C8。React 需要渲染这个组件,但是由于其返回的 React 元素和之前渲染的相同,所以不需要更新 DOM。

    显而易见,你看到 React 只改变了 C6 的 DOM。对于 C8,通过对比了渲染的 React 元素跳过了渲染。而对于 C2 的子节点和 C7,由于 shouldComponentUpdate 使得 render 并没有被调用。因此它们也不需要对比元素了。

    类组件React.PureComponent与函数组件memo

    通过shouldComponentUpdate可以避免不必要的渲染过程,从而达到性能上的优化。但是如果需要我们挨个对比props和state中的每个属性的话就太麻烦了,React提供了两种方式自动帮我们完成shouldComponentUpdate中的工作,类组件只要继承React.PureComponent就可以,函数组件提供了memo方法。

    //三种方式
    class CounterButton extends React.Component {
      constructor(props) {
        super(props);
        this.state = {count: 1};
      }
    
      shouldComponentUpdate(nextProps, nextState) {
        if (this.props.color !== nextProps.color) {
          return true;
        }
        if (this.state.count !== nextState.count) {
          return true;
        }
        return false;
      }
    
      render() {
        return (
          <button
            color={this.props.color}
            onClick={() => this.setState(state => ({count: state.count + 1}))}>
            Count: {this.state.count}
          </button>
        );
      }
    }
    
    
    class CounterButton extends React.PureComponent {
      constructor(props) {
        super(props);
        this.state = {count: 1};
      }
    
      render() {
        return (
          <button
            color={this.props.color}
            onClick={() => this.setState(state => ({count: state.count + 1}))}>
            Count: {this.state.count}
          </button>
        );
      }
    }
    
    
    const CounterButton = props => {
      const [count, setCount] = useState(1);
    
      return (
        <button color={props.color} onClick={() => setCount(count => count + 1)}>
          Count: {count}
        </button>
      );
    };
    
    export default React.memo(CounterButton);
    
    

    那有没有一种方式,使用浅比较就可以得出哪部分是改变的数据节点呢?

    immutable 数据结构

    我们还是通过一小段代码来认识什么是immutable数据,这里使用的是Immer这个库。

      import produce from 'immer';
      
      const a = [{ todo: 'Learn js' }, { todo: 'Learn react' }];
      const b = produce(a, draftState => {
        draftState[1].todo = 'Learn vue';
      });
    
      console.log(a === b); //false
      console.log(a[0] === b[0]); //true
      console.log(a[1] === b[1]); //false
      console.log(a[1].todo === b[1].todo); //false
    
    

    这里可以看到未改变的引用类型内存地址未发生改变,保证了旧节点的可用且不变,而改变了的节点,它和与它相关的所有上级节点都更新。如图所示:

    immutable data 是如何优化 React 性能的

    这样就避免了深拷贝带来的极大的性能开销问题,并且更新后返回了一个全新的引用,即使是浅比对也能感知到哪一部分数据需要更新。

    immer应用示例

    const [state, setState] = useState({
        id: 14,
        email: "stewie@familyguy.com",
        profile: {
          name: "Stewie Griffin",
          bio: "You know, the... the novel you've been working on",
          age:1
        }
      });
    
    function changeBio(newBio) {
        setState(current => ({
          ...current,
          profile: {
            ...current.profile,
            bio: newBio
          }
        }));
      }
    
    
    
    //使用 immer
    import { useImmer } from 'use-immer';
    
    const [state, setState] = useImmer({
        id: 14,
        email: "stewie@familyguy.com",
        profile: {
          name: "Stewie Griffin",
          bio: "You know, the... the novel you've been working on",
          age:1
        }
     });
    
    function changeBio(newBio) {
       setState(draft => {
          draft.profile.bio = newBio;
        });
      }
    

    减少了解构语法是不是清爽了很多,当然随着数据结构进一步复杂,immer优势也会进一步体现。

    感谢大家的阅读。


    起源地 » immutable data 是如何优化 React 性能的

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元