zoukankan      html  css  js  c++  java
  • React 组件性能优化探索实践

    转自:http://www.tuicool.com/articles/Ar6Zruq

    React本身就非常关注性能,其提供的虚拟DOM搭配上Diff算法,实现对DOM操作最小粒度的改变也是非常的高效。然而其组件渲染机制,也决定了在对组件进行更新时还可以进行更细致的优化。

    react组件渲染

    react的组件渲染分为初始化渲染和更新渲染。

    在初始化渲染的时候会调用根组件下的所有组件的render方法进行渲染,如下图(绿色表示已渲染,这一层是没有问题的):

    但是当我们要更新某个子组件的时候,如下图的绿色组件(从根组件传递下来应用在绿色组件上的数据发生改变):

    我们的理想状态是只调用关键路径上组件的render,如下图:

    但是react的默认做法是调用所有组件的render,再对生成的虚拟DOM进行对比,如不变则不进行更新。这样的render和虚拟DOM的对比明显是在浪费,如下图(黄色表示浪费的render和虚拟DOM对比)

    那么如何避免发生这个浪费问题呢,这就要牵出我们的 shouldComponentUpdate

    shouldComponentUpdate

    react在每个组件生命周期更新的时候都会调用一个 shouldComponentUpdate(nextProps, nextState) 函数。它的职责就是返回true或false,true表示需要更新,false表示不需要,默认返回为true,即便你没有显示地定义 shouldComponentUpdate 函数。这就不难解释上面发生的资源浪费了。

    为了进一步说明问题,我们再引用一张官网的图来解释,如下图( SCU表示shouldComponentUpdate,绿色表示返回true(需要更新),红色表示返回false(不需要更新);vDOMEq表示虚拟DOM比对,绿色表示一致(不需要更新),红色表示发生改变(需要更新)):

    根据渲染流程,首先会判断shouldComponentUpdate(SCU)是否需要更新。如果需要更新,则调用组件的render生成新的虚拟DOM,然后再与旧的虚拟DOM对比(vDOMEq),如果对比一致就不更新,如果对比不同,则根据最小粒度改变去更新DOM;如果SCU不需要更新,则直接保持不变,同时其子元素也保持不变。

    C1根节点,绿色SCU (true),表示需要更新,然后vDOMEq红色,表示虚拟DOM不一致,需要更新。

    C2节点,红色SCU (false),表示不需要更新,所以C4,C5均不再进行检查

    C3节点同C1,需要更新

    C6节点,绿色SCU (true),表示需要更新,然后vDOMEq红色,表示虚拟DOM不一致,更新DOM。

    C7节点同C2

    C8节点,绿色SCU (true),表示需要更新,然后vDOMEq绿色,表示虚拟DOM一致,不更新DOM。

    为了避免一定程度的浪费,react官方还在0.14版本中加入了无状态组件,如下:

    // es5
    function HelloMessage(props) {  
       return <div>Hello {props.name}</div>;
    }
    

      

    // es6
    const HelloMessage = (props) => <div>Hello {props.name}</div>;
    

      

    具体可参考官网: Reusable Components

    既然明白了这关键所在,现在是时候向我们的大大小小一箩筐组件开刀了。

    牛刀小试,直接把一些不需要更新的组件返回false

    下面我们以音量图标为例,这是一个svg图标,不需要更新,所以直接return false

    import React, {Component} from 'react';
    class Mic extends Component {
        constructor(props) {      
            super(props);
        }
        shouldComponentUpdate() {        
              return false;
        }
        render() {        
    return (           
     <svg className="icon-svg icon-mic" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" aria-labelledby="title">
                    <title>mic</title>
                    <path className="path1" d="M15 22c2.761 0 5-2.239 5-5v-12c0-2.761-2.239-5-5-5s-5 2.239-5 5v12c0 2.761 2.239 5 5 5zM22 14v3c0 3.866-3.134 7-7 7s-7-3.134-7-7v-3h-2v3c0 4.632 3.5 8.447 8 8.944v4.056h-4v2h10v-2h-4v-4.056c4.5-0.497 8-4.312 8-8.944v-3h-2z"></path>
                </svg>
            )
        }
    }
    
    export default Mic;    
    

      

    登堂入室,对数据进行对比确定是否需要更新

    先来个官网的例子,通过判断id是否改变来确定是否需要更新:

    shouldComponentUpdate: function(nextProps, nextState) {  
        return nextProps.id !== this.props.id;
    }
    

      看起来也没那么玄乎,直接一个 !== 对比下就ok了,那是不是所有的都可以这样直接对比就可以呢? 我们先来看下js的两个数据类型(原始类型与引用类型)的各自比较

    // 原始类型var a = 'hello the';var b = a;
    b = b + 'world';console.log(a === b);  // false// 引用类型var c = ['hello', 'the'];var d = c;
    d.push('world');console.log(c === d); // true
    

      

    我们可以看到a和b不等,但是c和d是一样一样的,这修改了d,也直接修改了c,那还怎么对比(关于原始类型与引用类型的区别这里就不说明了)。

    现在看来我们得分情况处理了,原始类型数据和引用类型数据得采用不同的办法处理。

    原始类型数据

    这没什么好说的,直接比对就是了。但是每个人都是想偷懒的,这要是每个组件都要这样去写下也挺麻烦的,于是react官方有了插件帮我们搞定这事:

    PureRenderMixin (es5的插件)

    var PureRenderMixin = require('react-addons-pure-render-mixin');
    React.createClass({
      mixins: [PureRenderMixin],
    
      render: function() {    
           return <div className={this.props.className}>foo</div>;
      }
    });
    

      Shallow Compare (es6的插件)

    var shallowCompare = require('react-addons-shallow-compare');
    export class SampleComponent extends React.Component {
      shouldComponentUpdate(nextProps, nextState) {    
           return shallowCompare(this, nextProps, nextState);
      }
    
      render() {   
            return <div className={this.props.className}>foo</div>;
      }
    } 
    

      

    引用类型数据

    既然引用类型数据一直返回true,那就得想办法处理,能不能把前后的数据变成不一样的引用呢,那样不就不相等了吗?于是就有了我们的不可变数据。

    react官方提供了一个 Immutability Helpers

     

    const newValue = {
        ...oldValue    // 在这里做你想要的修改};// 快速检查 —— 只要检查引用newValue === oldValue; // false// 如果你愿意也可以用 Object.assign 语法const newValue2 = Object.assign({}, oldValue);
    
    newValue2 === oldValue; // false

    然后在 shouldComponentUpdate 中进行比对

    shouldComponentUpdate(nextProps) {    

      return isObjectEqual(this.props, nextProps); }

    我们目前采用的是在reducer里面更新数据使用 Object.assign({}, state, {newkey: newValue} (数据管理采用redux),然后在组件里面根据某个具体的字段判断是否更新,如title或id等,而不是判断整个对象:
    shouldComponentUpdate: function(nextProps, nextState){    
         return nextProps.title !== this.props.title;
    }
    

      

     
  • 相关阅读:
    在peoplecode中直接调用SQR
    想起了李雷和韩梅梅
    结婚两周年纪念
    Unix Command Summary
    在PeopleSoft中如何隐藏菜单,导航栏,以及其他定制化链接
    那些朋友们
    整天工作的人为何当不了富翁
    ActiveX简单介绍
    SQL UNION
    Java程序设计问答大全(一)
  • 原文地址:https://www.cnblogs.com/laneyfu/p/6225409.html
Copyright © 2011-2022 走看看