美文网首页Front End
[React] render中进行diff

[React] render中进行diff

作者: 何幻 | 来源:发表于2017-01-22 18:11 被阅读52次

    场景

    import React, { Component } from 'react';
    import ReactDOM from 'react-dom';
    
    class A extends Component {
        componentWillMount() {
            console.log('A: componentWillMount');
        }
    
        render() {
            console.log('A: render');
    
            return (
                <div>
                    <div>456</div>
                    <div>A{this.props.a}</div>
                </div>
            );
        }
    
        componentDidMount() {
            console.log('A: componentDidMount');
        }
    
        componentWillReceiveProps() {
            console.log('A: componentWillReceiveProps');
        }
    
        shouldComponentUpdate() {
            console.log('A: shouldComponentUpdate');
            return true;
        }
    
        componentWillUpdate() {
            console.log('A: componentWillUpdate');
        }
    
        // render
    
        componentDidUpdate() {
            console.log('A: componentDidUpdate');
        }
    }
    
    class Page extends Component {
        state = {
            a: 1
        };
    
        componentWillMount() {
            console.log('Page: componentWillMount');
        }
    
        render() {
            console.log('Page: render');
    
            return (
                <div>
                    <div>123</div>
                    <A a={this.state.a} />
                </div>
            );
        }
    
        componentDidMount() {
            console.log('Page: componentDidMount');
    
            setTimeout(() => {
                console.warn('Page: setState');
                this.setState({
                    a: 1
                });
            }, 2000);
        }
    
        componentWillReceiveProps() {
            console.log('Page: componentWillReceiveProps');
        }
    
        shouldComponentUpdate() {
            console.log('Page: shouldComponentUpdate');
            return true;
        }
    
        componentWillUpdate() {
            console.log('Page: componentWillUpdate');
        }
    
        // render
    
        componentDidUpdate() {
            console.log('Page: componentDidUpdate');
        }
    }
    
    ReactDOM.render(
        <Page />,
        document.getElementById('app')
    );
    

    解答

    1. 日志分析

    (1)即使没有改变state,也会调用shouldComponentUpdate
    this.setState({a:2});(改变了state)和this.setState({a:1});(没有改变state)日志结果一样。

    // 当前组件和子组件shouldComponentUpdate都为true
    
    Page: componentWillMount
    Page: render
        A: componentWillMount
        A: render
        A: componentDidMount
    Page: componentDidMount
    
    Page: setState
    Page: shouldComponentUpdate ---- true
    Page: componentWillUpdate
    Page: render
        A: componentWillReceiveProps
        A: shouldComponentUpdate ---- true
        A: componentWillUpdate
        A: render
        A: componentDidUpdate
    Page: componentDidUpdate
    

    (2)如果A组件的shouldComponentUpdate返回false
    那么A组件的componentWillUpdate render componentDidUpdate就都不执行了。

    // 子组件shouldComponentUpdate为false
    
    Page: componentWillMount
    Page: render
        A: componentWillMount
        A: render
        A: componentDidMount
    Page: componentDidMount
    
    Page: setState
    Page: shouldComponentUpdate ---- true
    Page: componentWillUpdate
    Page: render
        A: componentWillReceiveProps
        A: shouldComponentUpdate ---- false
    Page: componentDidUpdate
    

    注:这一点只是在当前React版本中生效

    Currently, if shouldComponentUpdate() returns false, then componentWillUpdate(), render(), and componentDidUpdate() will not be invoked. Note that in the future React may treat shouldComponentUpdate() as a hint rather than a strict directive, and returning false
    may still result in a re-rendering of the component.
    —— React.Component: shouldComponentUpdate()

    (3)如果组件PageshouldComponentUpdate返回false
    那么Page组件的componentWillUpdate render componentDidUpdate就都不执行了。

    // 当前组件的shouldComponentUpdate为false
    
    Page: componentWillMount
    Page: render
        A: componentWillMount
        A: render
        A: componentDidMount
    Page: componentDidMount
    
    Page: setState
    Page: shouldComponentUpdate ---- false
    

    注意,A组件的componentWillReceiveProps shouldComponentUpdate componentWillUpdate render componentDidUpdate也都不执行了。
    因为,子组件的componentWillReceiveProps是在父组件render后执行的,子组件componentDidUpdate后,父组件才会componentDidUpdate

    2. DOM更新

    在调试工具中查看哪些DOM被重新渲染
    (1)打开chrome开发者工具
    (2)按Esc,打开console
    (3)点击console左边的按钮,勾选Rendering
    (4)勾选Paint Flashing


    我们发现,即使render函数被调用,DOM也不是全部更新,而是根据diff算法来更新。

    3. 结论

    只要执行this.setState,则当前组件的shouldComponentUpdate就会被调用。

    如果当前组件的shouldComponentUpdate返回true
    则子组件的componentWillReceiveProps shouldComponentUpdate将被调用,不论子组件的props是否被改变
    如果当前组件的shouldComponentUpdate返回false
    则子组件的componentWillReceiveProps shouldComponentUpdate componentWillUpdate render componentDidUpdate被调用。

    如果子组件的shouldComponentUpdate返回true,则调用componentWillUpdate render,然后通过diff算法更新DOM,最后调用componentDidUpdate
    如果子组件的shouldComponentUpdate返回false,则子组件的componentWillUpdate render componentDidUpdate都不被调用。


    参考

    React.Component: The Component Lifecycle
    Reconciliation: The Diffing Algorithm

    相关文章

      网友评论

        本文标题:[React] render中进行diff

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