1. 渲染:生成用于显示的对象,以及将这些对象形成真实的 DOM 对象;

  2. React 元素:通过 React.createElement 创建 (语法糖:JSX)

    <div><h1>标题</h1></div>
    
    <App />
    
  3. React 节点:专门用于渲染到 UI 界面的对象,React 会通过 React 元素,创建 React 节点,ReactDOM 一定是通过 React 节点来进行渲染的 (react 内部进行转换,将元素转换成节点)

  4. 节点类型

    1. React DOM 节点:创建该节点的 React 元素类型是一个字符串;
    2. React 组件节点:创建该节点的 React 元素类型是一个函数或是一个类;
    3. React 文本节点:由字符串、数字创建的;
    4. React 空节点:由 nullundefinedfalsetrue
    5. React 数组节点:该节点由一个数组创建;
  5. 真实 DOM:通过 document.createElement 创建的 dom 元素;

首次渲染 (新节点渲染)

  1. 通过参数的值创建节点,根据不同的节点,做不同的事情;

    1. 文本节点:通过 document.createTextNode 创建真实的文本节点;
    2. 空节点:什么都不做
    3. 数组节点:遍历数组,将数组每一项递归创建节点 (回到第 1 步进行反复操作,直到遍历结束)
    4. DOM 节点:通过 document.createElement 创建真实的 DOM 对象,然后立即设置该真实 DOM 元素的各种属性,然后遍历对应 React 元素的 children 属性,递归操作 (回到第 1 步进行反复操作,直到遍历结束)
    5. 函数组件节点:调用函数 (该函数必须返回一个可以生成节点的内容),将该函数的返回结果递归生成节点 (回到第 1 步进行反复操作,直到遍历结束)
    6. 类组件节点
      1. 创建该类的实例
      2. 立即调用对象的生命周期方法:static getDerivedStateFromProps
      3. 运行该对象的 render 方法,拿到节点对象 (将该节点递归操作,回到第 1 步进行反复操作)
      4. 将该组件的 componentDidMount 加入到执行队列 ,当整个虚拟 DOM 树全部构建完毕,并且将真实的 DOM 对象加入到容器中后,执行该队列;
  2. 生成出虚拟 DOM 树之后,将该树保存起来,以便后续使用;

  3. 将之前生成的真实的 DOM 对象,加入到容器中;

DOM 节点渲染示例

  1. 示例代码:

    const app = <div className="assaf">
        <h1>
            标题
            {["abc", null, <p>段落</p>]}
        </h1>
        <p>
            {undefined}
        </p>
    </div>;
    ReactDOM.render(app, document.getElementById('root'));
    
  2. 生成的虚拟 DOM 树:

函数组件渲染示例

  1. 示例代码:

    function Comp1(props) {
        return <h1>Comp1 {props.n}</h1>
    }
    
    function App(props) {
        return (
            <div>
                <Comp1 n={5} />
            </div>
        )
    }
    
    const app = <App />;
    ReactDOM.render(app, document.getElementById('root'));
    
  2. 生成的虚拟 DOM 树:

类组件渲染示例

  1. 示例代码:

    class Comp1 extends React.Component {
        render() {
            return (
                <h1>Comp1</h1>
            )
        }
    }
    
    class App extends React.Component {
        render() {
            return (
                <div>
                    <Comp1 />
                </div>
            )
        }
    }
    
    const app = <App />;
    ReactDOM.render(app, document.getElementById('root'));
    
  2. 生成的虚拟 DOM 树:

节点更新

更新的场景:

  1. 重新调用ReactDOM.render,触发根节点更新

  2. 在类组件的实例对象中调用setState,会导致该实例所在的节点更新

节点的更新

  • 如果调用的是ReactDOM.render,进入根节点的对比(diff)更新

  • 如果调用的是setState

      1. 运行生命周期函数,static getDerivedStateFromProps
      1. 运行shouldComponentUpdate,如果该函数返回false,终止当前流程
      1. 运行render,得到一个新的节点,进入该新的节点的对比更新
      1. 将生命周期函数getSnapshotBeforeUpdate加入执行队列,以待将来执行
      1. 将生命周期函数componentDidUpdate加入执行队列,以待将来执行

后续步骤:

  1. 更新虚拟DOM树

  2. 完成真实的DOM更新

  3. 依次调用执行队列中的componentDidMount

  4. 依次调用执行队列中的getSnapshotBeforeUpdate

  5. 依次调用执行队列中的componentDidUpdate

对比更新

将新产生的节点,对比之前虚拟DOM中的节点,发现差异,完成更新

问题:对比之前DOM树中哪个节点

React为了提高对比效率,做出以下假设

  1. 假设节点不会出现层次的移动(对比时,直接找到旧树中对应位置的节点进行对比)

  2. 不同的节点类型会生成不同的结构

    1. 相同的节点类型:节点本身类型相同,如果是由React元素生成,type值还必须一致
    2. 其他的,都属于不相同的节点类型
  3. 多个兄弟通过唯一标识(key)来确定对比的新节点

key值的作用:用于通过旧节点,寻找对应的新节点,如果某个旧节点有key值,则其更新时,会寻找相同层级中的相同key值的节点,进行对比。

key值应该在一个范围内唯一(兄弟节点中),并且应该保持稳定

找到了对比的目标

判断节点类型是否一致

  • 一致

根据不同的节点类型,做不同的事情

空节点:不做任何事情

DOM节点

  1. 直接重用之前的真实DOM对象

  2. 将其属性的变化记录下来,以待将来统一完成更新(现在不会真正的变化)

  3. 遍历该新的React元素的子元素,递归对比更新

文本节点

  1. 直接重用之前的真实DOM对象

  2. 将新的文本变化记录下来,将来统一完成更新

组件节点

函数组件:重新调用函数,得到一个节点对象,进入递归对比更新

类组件

  1. 重用之前的实例

  2. 调用生命周期方法getDerivedStateFromProps

  3. 调用生命周期方法shouldComponentUpdate,若该方法返回false,终止

  4. 运行render,得到新的节点对象,进入递归对比更新

  5. 将该对象的getSnapshotBeforeUpdate加入队列

  6. 将该对象的componentDidUpdate加入队列

数组节点:遍历数组进行递归对比更新

  • 不一致

整体上,卸载旧的节点,全新创建新的节点

创建新节点

进入新节点的挂载流程

卸载旧节点

  1. 文本节点、DOM节点、数组节点、空节点、函数组件节点:直接放弃该节点,如果节点有子节点,递归卸载节点

  2. 类组件节点

    1. 直接放弃该节点
    2. 调用该节点的componentWillUnMount函数
    3. 递归卸载子节点

没有找到对比的目标

新的DOM树中有节点被删除

新的DOM树中有节点添加

  • 创建新加入的节点

  • 卸载多余的旧节点

打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 首次渲染 (新节点渲染)
    1. 1.1. DOM 节点渲染示例
    2. 1.2. 函数组件渲染示例
    3. 1.3. 类组件渲染示例
  2. 2. 节点更新
    1. 2.1. 对比更新
      1. 2.1.1. 找到了对比的目标
      2. 2.1.2. 没有找到对比的目标