目录
浏览器渲染
虚拟DOM
diff算法对比
React.createElement()
总结
页面JSX渲染
渲染过程(整体)
JSX渲染流程
JSX底层处理机制
第一步:JSX为虚拟DOM
第二步:构建的virtualDOM渲染为真实DOM
前面首先对浏览器渲染、虚拟DOM概念理解,JSX底层处理机制是对JSX编译到修改动作内部发生的原理。
浏览器渲染
下图是浏览器的渲染图,下面会介绍虚拟DOM
可以看到这里,浏览器渲染需要三个引擎,HTML引擎、CSS引擎、JS引擎,页面渲染直接和html、css相关,生成一个DOM树和css规则树,最后合成一个渲染树,最后根据渲染树布局和绘制的操作,成本最高的布局,其次会绘制,绘制无法避免,但是我们可以减少绘制的次数,如果是jquery的话,每循环一次,整个页面就会刷新,效率很低。
虚拟DOM
diff算法对比
虚拟DOM是为了避免渲染(布局和绘制)的次数,发明了diff算法,这里将原生的DOM用JS来表示,在操作的时候有一个对比方式,如下图:
查看这个图可以发现,diff算法只是对dom内部针对不同元素做不同的刷新,如果元素变化就进行替换操作,内部元素改变就进行文本变化,参数变化只是进行props操作。简单意思是什么变化就进行什么操作(变化之后统一操作)。
浏览器做DOM操作的成本太高,故而采用虚拟DOM。
实现虚拟dom可以使用createElement实现。
注意:这里具体实现虚拟DOM是通过React.createElement()创建。document.createElement创建真实DOM。
React.createElement()
这里实现一个简单的React.createElement()例子:
简单标签实现的方式
<!-- 需要实现的标签 -->
<h1 id="title">Hello,React!</h1>
// React.createElement()实现的代码
const VDiv = React.createElement('h1',{id:'title'},'Hello,React!');
嵌套标签实现的方式
<!-- 需要实现的标签 -->
<h1 id="title"><span>Hello,React!</span></h1>
// React.createElement()实现的代码
const VDiv = React.createElement('h1',{id:'title'},React.createElement('span',{},'Hello,React!'));
然而,Jsx实现的的代码是
const VDiv = (
<h1 id="title">
<span>Hello,React!</span>
</h1>
)
经过对比可以发现:React.createElement()创建虚拟DOM太过于繁琐,JSX的作用是为让开发人员更简单的创建虚拟DOM,这里经过翻译之后的代码运行到浏览器之后一定是React.createElement()实现的代码部分。
Jsx创建DOM的实现方法是React.createElement()方法的语法糖。
总结
1.创建虚拟DOM的两种方式:
- 纯js React.createElement()
- Jsx
在React内一般使用Jsx。
2.虚拟DOM的本质
- 本质是Object类型的对象(一般对象)
- 虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在使用,无需真实DOM上那么多属性(轻和重指的是属性的多少)
- 虚拟DOM最终会被React转换为真实DOM,展示在页面上。
页面JSX渲染
渲染过程(整体)
编译后的结果
// JSX
<div className="aaa bbb">
123
<div>456</div>
</div>
// 经过 babel-preset-react-app 编译后的代码
React.createElement(
"div",
{ className: "aaa bbb" },
"123",
React.createElement(
"div",
null,
"456"
)
)
JSX渲染流程
第一次页面加载 渲染内容
<div className="demo-box">
<h2 className="title">{title}</h2>
<span>{x}</span>
</div>
virtualDOM虚拟DOM对象,在第一次渲染完毕后,会把创建的virtualDOM 缓存起来,存储为oldvirtualDOM , 然后将虚拟DOM渲染为真实页面
修改后:
修改JSX内的数据或者内容时,它会把JSX重新编译为全新的virtualDOM【重新编译一遍】
编译为一套新的 virtualDOM ,新的编译好之后,和原来的 oldvirtualDOM 进行对比【使用diff算法】 --->生成PATCH补丁包【变化的那部分】,只修改变化的那部分,只渲染补丁包 然后生成真实DOM
JSX底层处理机制
第一步:JSX为虚拟DOM
把我们编写的JSX语法,编译为虚拟DOM对象「virtualDOM」
虚拟DOM对象:框架自己内部构建的一套对象体系(对象的相关成员都是React内部规定的),基于这些属性描述出,我们所构建视图中的,DOM节点的相关特征!!
@1 基于 babel-preset-react-app 把JSX编译为 React.createElement(...) 这种格式!!
只要是元素节点,必然会基于createElement进行处理!
React.createElement(ele,props,...children)
+ ele:元素标签名「或组件」
+ props:元素的属性集合(对象)「如果没有设置过任何的属性,则此值是null」
+ children:第三个及以后的参数,都是当前元素的子节点
@2 再把 createElement 方法执行,创建出virtualDOM虚拟DOM对象「也有称之为:JSX元素、JSX对象、ReactChild对象...」!!
virtualDOM = {
$$typeof: Symbol(react.element),
ref: null,
key: null,
type: 标签名「或组件」,
// 存储了元素的相关属性 && 子节点信息
props: {
元素的相关属性,
children:子节点信息「没有子节点则没有这个属性、属性值可能是一个值、也可能是一个数组」
}
}
第二步:构建的virtualDOM渲染为真实DOM
真实DOM:浏览器页面中,最后渲染出来,让用户看见的DOM元素!!
基于ReactDOM中的render方法处理的!!
v16
ReactDOM.render(
<>...</>,
document.getElementById('root')
);
v18
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<>...</>
);
补充说明:第一次渲染页面是直接从virtualDOM->真实DOM;但是后期视图更新的时候,需要经过一个DOM-DIFF的对比,计算出补丁包PATCH(两次视图差异的部分),把PATCH补丁包进行渲染!!