渲染原理
术语描述:
渲染: 生成用于显示的对象,以及将这些对象形成真实的 DOM 对象
-
React 元素 (React Element)
:通过React.createElement
创建 (语法糖:JSX
表达式创建);如:<div>React Element</div>
、<App />
-
React 节点 (React Component)
:专门用于渲染到 UI 界面的对象 (Virtual DOM
),React 会通过React 元素
创建 React 节点,ReactDOM
一定是根据 React 节点来渲染页面的节点类型:
React DOM 节点 (Virtual DOM Node)
:创建该节点的 React 与元素类型是一个字符串
("div"
,"h1'
等)React 组件节点 (React ComponentNode)
:创建该节点的 React 与元素类型是一个函数
或一个类
(比如我们封装的函数组件或类组件等
)React 文本节点 (React TextNode)
:由字符串、数字创建React 空节点 (React EmptyNode)
:null
,undefined
,false
,true
React 数组节点 (React ArrayNode)
:由数组创建
首次渲染 (新节点挂载阶段)
-
通过参数的值创建节点
ReactDOM.render(参数, MONT_NODE)
-
根据不同的节点,做不同的事情
- ①
文本节点
:通过document.createTextNode()
创建真实的文本节点 - ②
空节点
:不创建真实 DOM - ③
数组节点
:遍历数组,对每一项递归进行创建节点 (回第 ① 步,直到遍历结束) - ④
DOM 节点
:JSX 解析生成的对象 (React 元素),通过document.createElement()
创建真实 DOM 对象,然后遍历对应 React 元素的 children 属性(对象或数组),递归操作 (回第1步,直到遍历结束) - ⑤
组件节点
:函数组件
:调用函数 (该函数必须返回可以生成节点的内容),将该函数的返回结果递归生成节点 (同上);类组件
:创建该类的实例,立即调用对象的生命周期方法static getDerivedStateFromProps
,运行该对象的render
方法,得到节点对象,递归操作;将该组件的componentDidMount
加入到执行队列 (先进先执行),当整个虚拟 DOM 树全部构建完毕,并且将真实的 DOM 对象加入到容器中后,遍历该队列执行
- ①
-
生成虚拟 DOM 树后,将该树保存起来,以便后续使用 (diff)
-
将之前生成的真实 DOM 对象,加入到页面的容器中
更新节点
节点更新时机:
-
重新调用
ReactDOM.render()
方法,完全重新生成节点树 (虚拟 DOM 树),触发根节点的更新 -
在类组件中调用
this.setState()
更新状态,会导致该实例所在的节点更新 -
hook 涉及的更新后续再说~
对比更新
-
如果调用
ReactDOM.render()
方法,对比更新根节点(diff
) -
如果调用
this.setState()
方法- 运行生命周期函数
static getDerivedStateFromProps
- 运行生命周期函数
shouldComponentUpdate
,若返回 false,终止当前流程
- 运行 render,得到一个新的节点,进入该新节点的 对比更新
- 将生命周期函数
getSnapshotBeforeUpdate
加入执行队列,等待执行 - 将生命周期函数
componentDidUpdate
加入执行队列 (与上面这步不是同一个队列),等待执行
- 运行生命周期函数
对比更新的后续步骤:
- 更新虚拟 DOM 树
- 完成真实 DOM 的更新
- 依次调用执行队列中的
compoentDidMount
(产生的新组件挂载) - 依次调用执行队列中的
getSnapshotBeforeUpdate
- 依次调用执行队列中的
componentDidUpdate
- 依次调用执行队列中的
componentWillUnmount
(被移除的子节点才会推入队列)
对比更新:将新产生的节点,与之前虚拟 DOM 中的节点进行对比,发现差异,进行更新
对比假设
React 为了提高对比效率,做出以下假设:
-
React 节点的位置不会进行层级的移动 (对比时,直接找到旧树中对应位置的节点进行对比)
-
不同的节点类型会生成不同的结构
- 相同的节点类型:节点本身类型相同 (如文本类型,DOM节点);如果是组件节点,组件类型也得相同 (类 / 函数);若是由 React 元素生成,type 值必须是一致的
-
多个兄弟节点通过唯一标识 (key) 来确定对比的新节点
- key 值用于通过旧节点来寻找应该对比的新节点,如果某个旧节点有 key 值,则其更新时,会寻找相同层级中具有相同 key 值的节点 (若未找到,则进入未找到对比节点的流程)
找到对比节点
判断节点类型是否一致
-
一致:根据不同的节点做不同的事:
- ①
空节点
:不做任何事; - ②
DOM 节点
:重用之前生成的真实 DOM 对象,将其属性的变化进行记录 (此时不会更新 DOM),遍历该新的 React 元素的子元素,递归对比更新; - ③
文本节点
:直接重用之前的真实 DOM 对象,记录变化的nodeValue
值; - ④
函数组件节点
:重新调用函数,得到一个节点对象,进入递归对比更新; - ⑤
类组件节点
:重用之前的实例,依次调用static getDerivedStateFromProps
,shouldComponentUpdate
(若返回false,终止对比),否则运行render
,进入递归对比更新,将该对象的getSnapshotBeforeUpdate
,componentDidUpdate
加入相应的队列,等待执行; - ⑥
数组节点
:遍历数组,进行递归对比更新
- ①
-
不一致:整体上,卸载旧的节点,重新创建新的节点 (
先创建新节点,后卸载旧节点
)。旧节点:- ①
空节点
,文本节点
,DOM 节点
,数组节点
,函数组件节点
:直接舍弃旧节点,创建新节点 (进入新节点挂载阶段
); - ②
类组件节点
:直接舍弃旧节点,调用该节点的componentWillUnmount
函数,递归卸载子节点
- ①
未找到对比目标
新的虚拟 DOM 树中有 新的节点删除或添加
,即:创建新加入的节点,卸载多余的节点 (还是上面挂载跟卸载的流程)
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!