React-Diff算法

2020-08-26  本文已影响0人  zhulichao

Diff算法

React.js 相对于直接操作原生DOM有很大的性能优势,很大程度上都要归功于virtual DOM的batching和diff。batching把所有的DOM操作搜集起来,一次性提交给真实的DOM。diff算法时间复杂度也从标准的的Diff算法的O(n3)降到了O(n)。React.js将标准的的Diff算法复杂度O(n3)直接降低到O(n)基于的假设:

在比较两个React节点树时,React首先比较根元素,随后的行为取决于根元素类型:

React的Diff算法实际上只会对树进行逐层比较。在实现自己的组件时,保持稳定的DOM结构会有助于性能的提升。例如,我们有时可以通过CSS隐藏或显示某些节点,而不是真的移除或添加DOM节点。

传统算法O(n^3)(转) React算法O(n)(转)

key属性在diff算法中的作用

使用浏览器的MutationObserver的功能,对页面上元素的改变进行监听,示例代码如下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="react.js"></script>
    <script src="react-dom.js"></script>
    <script src="jquery.js"></script>
    <script src="babel.min.js"></script>
  </head>
  <body>
    <div id="container"></div>
    <style>

    </style>
    <script type="text/javascript">
        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
        var observeMutationSupport = !!MutationObserver;
        var changedNodes = [];
        if(observeMutationSupport) {
            var observer = new MutationObserver(function(mutations) {
                changedNodes = mutations;
                mutations.forEach(function(mutation){
                    console.log(mutation);
                });
            });
            var options = {
                'childList': true, // 子节点的变动
                'attributes': true, // 属性的变动
                'characterData': true, // 节点内容或节点文本的变动
                'subtree': true, // 所有后代节点的变动
                'attributeOldValue': true, // 表示观察attributes变动时,是否需要记录变动前的属性值
                'characterDataOldValue': true // 表示观察characterData变动时,是否需要记录变动前的值
            };
            observer.observe(document.body, options);
        }
    </script>
    <script type="text/babel">
        function LifeCycle(name) {
            var obj = {
                name: name
            };
            for(var n in Cycle){
                obj[n] = Cycle[n];
            }
            return obj;
        }
        var Cycle = {
            getInitialState: function() {
                console.log(this.name, 'getInitialState');
                return {};
            },
            getDefaultProps: function() {
                console.log(this.name, 'getDefaultProps');
                return {};
            },
            componentWillMount: function() {
                console.log(this.name, 'componentWillMount');
            },
            componentDidMount: function() {
                console.log(this.name, 'componentDidMount');
            },
            componentWillReceiveProps: function() {
                console.log(this.name, 'componentWillReceiveProps');
            },
            shouldComponentUpdate: function() {
                console.log(this.name, 'shouldComponentUpdate');
                return true;
            },
            componentWillUpdate: function() {
                console.log(this.name, 'componentWillUpdate');
            },
            componentDidUpdate: function() {
                console.log(this.name, 'componentDidUpdate');
            },
            componentWillUnmount: function() {
                console.log(this.name, 'componentWillUnmount');
            }
        };
        // 定义Ctrip组件
        var Ctrip = React.createClass({
            mixins: [LifeCycle('Ctrip')],
            render: function() {
                console.log('Ctrip', 'render');
                return (
                    <ul>
                        <li>ctrip</li>
                        <li>
                            <ul>
                                {this.props.children}
                            </ul>
                        </li>
                    </ul>
                );
            }
        });
        // 定义Elong组件
        var Elong = React.createClass({
            mixins: [LifeCycle('Elong')],
            render: function() {
                console.log('Elong', 'render');
                return (
                    <li>elong</li>
                );
            }
        });
        // 定义Qunar组件
        var Qunar = React.createClass({
            mixins: [LifeCycle('Qunar')],
            render: function() {
                console.log('Qunar', 'render');
                return (
                    <li>qunar</li>
                );
            }
        });
        // 定义Ly组件
        var Ly = React.createClass({
            mixins: [LifeCycle('Ly')],
            render: function () {
                console.log('Ly', 'render');
                return (
                    <li>ly</li>
                );
            }
        });

        console.log('------------first------------');
        ReactDOM.render(
            <Ctrip><Elong></Elong><Qunar></Qunar></Ctrip>,
            document.getElementById('container')
        );
        setTimeout(function() {
            console.log('------------second------------');
            ReactDOM.render(
                <Ctrip><Elong></Elong><Ly></Ly><Qunar></Qunar></Ctrip>,
                document.getElementById('container')
            );
        }, 1000);
    </script>
  </body>
</html>

运行结果如图

常规的做法就是将Elong和Qunar组件先删除,然后一次创建和插入Elong、Ly和Qunar组件。通过运行结果可以看出,在React中,Elong组件不变,先将Qunar组件进行删除,然后在创建并插入Ly组件,最后再创建并插入Qunar组件,比常规的做法省去了对Elong组件的删除操作。下面再将主逻辑代码稍作调整,给每个组件添加key属性:

...
console.log('------------first------------');
ReactDOM.render(
    <Ctrip><Elong key="Elong"></Elong><Qunar key="Qunar"></Qunar></Ctrip>,
    document.getElementById('container')
);
setTimeout(function() {
    console.log('------------second------------');
    ReactDOM.render(
        <Ctrip><Elong key="Elong"></Elong><Ly key="Ly"></Ly><Qunar key="Qunar"></Qunar></Ctrip>,
        document.getElementById('container')
    );
}, 1000);
...
运行结果如图

这次的Diff算法与之前有很大不同,Elong组件不变,Qunar组件不变,只是在Qunar组件之前创建并插入了Ly组件。可见使用key属性可提高渲染性能。

上一篇 下一篇

猜你喜欢

热点阅读