目录

浏览器渲染

虚拟DOM

diff算法对比

React.createElement()

总结

页面JSX渲染

渲染过程(整体)

JSX渲染流程

JSX底层处理机制

第一步:JSX为虚拟DOM

第二步:构建的virtualDOM渲染为真实DOM


 

 前面首先对浏览器渲染、虚拟DOM概念理解,JSX底层处理机制是对JSX编译到修改动作内部发生的原理。

浏览器渲染

下图是浏览器的渲染图,下面会介绍虚拟DOM

 

elementPuls Popover虚拟触发使用教程_前端

可以看到这里,浏览器渲染需要三个引擎,HTML引擎、CSS引擎、JS引擎,页面渲染直接和html、css相关,生成一个DOM树和css规则树,最后合成一个渲染树,最后根据渲染树布局和绘制的操作,成本最高的布局,其次会绘制,绘制无法避免,但是我们可以减少绘制的次数,如果是jquery的话,每循环一次,整个页面就会刷新,效率很低。

虚拟DOM

diff算法对比

虚拟DOM是为了避免渲染(布局和绘制)的次数,发明了diff算法,这里将原生的DOM用JS来表示,在操作的时候有一个对比方式,如下图:

elementPuls Popover虚拟触发使用教程_JSX_02

查看这个图可以发现,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渲染

babel官网的在线开发

渲染过程(整体)

elementPuls Popover虚拟触发使用教程_前端_03

 

编译后的结果

// 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补丁包进行渲染!!