vue源码中的渲染过程是怎样的

4.1 Virtual DOM

4.1.1 浏览器的渲染流程

当浏览器接收到一个Html文件时,JS引擎和浏览器的渲染引擎便开始工作了。从渲染引擎的角度,它首先会将html文件解析成一个DOM树,与此同时,浏览器将识别并加载CSS样式,并和DOM树一起合并为一个渲染树。有了渲染树后,渲染引擎将计算所有元素的位置信息,最后通过绘制,在屏幕上打印最终的内容。JS引擎和渲染引擎虽然是两个独立的线程,但是JS引擎却可以触发渲染引擎工作,当我们通过脚本去修改元素位置或外观时,JS引擎会利用DOM相关的API方法去操作DOM对象,此时渲染引擎变开始工作,渲染引擎会触发回流或者重绘。下面是回流重绘的两个概念:

  • 回流: 当我们对DOM的修改引发了元素尺寸的变化时,浏览器需要重新计算元素的大小和位置,最后将重新计算的结果绘制出来,这个过程称为回流。
  • 重绘: 当我们对DOM的修改只单纯改变元素的颜色时,浏览器此时并不需要重新计算元素的大小和位置,而只要重新绘制新样式。这个过程称为重绘。

很显然回流比重绘更加耗费性能

通过了解浏览器基本的渲染机制,我们很容易联想到当不断的通过JS修改DOM时,不经意间会触发到渲染引擎的回流或者重绘,这个性能开销是非常巨大的。因此为了降低开销,我们需要做的是尽可能减少DOM操作。有什么方法可以做到呢?

4.1.2 缓冲层-虚拟DOM

虚拟DOM是为了解决频繁操作DOM引发性能问题的产物。虚拟DOM(下面称为Virtual DOM)是将页面的状态抽象为JS对象的形式,本质上是JS和真实DOM的中间层,当我们想用JS脚本大批量进行DOM操作时,会优先作用于Virtual DOM这个JS对象,最后通过对比将要改动的部分通知并更新到真实的DOM。尽管最终还是操作真实的DOM,但Virtual DOM可以将多个改动合并成一个批量的操作,从而减少 DOM 重排的次数,进而缩短了生成渲染树和绘制所花的时间。

我们看一个真实的DOM包含了什么:

浏览器将一个真实DOM设计得很复杂,不仅包含了自身的属性描述,大小位置等定义,也囊括了DOM拥有的浏览器事件等。正因为如此复杂的结构,我们频繁去操作DOM或多或少会带来浏览器的性能问题。而作为数据和真实DOM之间的一层缓冲,Virtual DOM 只是用来映射到真实DOM的渲染,因此不需要包含操作 DOM 的方法,它只要在对象中重点关注几个属性即可。

// 真实DOM
<div id="real"><span>dom</span></div>

// 真实DOM对应的JS对象
{
   
    tag: 'div',
    data: {
   
        id: 'real'
    },
    children: [{
   
        tag: 'span',
        children: 'dom'
    }]
}

4.2 Vnode

Vue在渲染机制的优化上,同样引进了virtual dom的概念,它是用Vnode这个构造函数去描述一个DOM节点。参考Vue3源码视频讲解:进入学习

4.2.1 Vnode构造函数

var VNode = function VNode (tag,data,children,text,elm,context,componentOptions,asyncFactory) {
   
    this.tag = tag; // 标签
    this.data = data;  // 数据
    this.children = children; // 子节点
    this.text = text;
    ···
    ···
  };

Vnode定义的属性差不多有20几个,显然用Vnode对象要比真实DOM对象描述的内容要简单得多,它只用来单纯描述节点的关键属性,例如标签名,数据,子节点等。并没有保留跟浏览器相关的DOM方法。除此之外,Vnode也会有其他的属性用来扩展Vue的灵活性。

源码中也定义了创建Vnode的相关方法。

4.2.2 创建Vnode注释节点

// 创建注释vnode节点
var createEmptyVNode = function (text) {
   
    if ( text === void 0 ) text = '';

    var node = new VNode();
    node.text = text;
    node.isComment = true; // 标记注释节点
    return node
};

4.2.3 创建Vnode文本节点

// 创建文本vnode节点
function createTextVNode (val) {
   
    return new VNode(undefined, undefined, undefined, String(val))
}

4.2.4 克隆vnode

function cloneVNode (vnode) {
   
    var cloned = new VNode(
      vnode.tag,
      vnode.data,
      vnode.children && vnode.children.slice(),
      vnode.text,
      vnode.elm,
      vnode.context,
      vnode.componentOptions,
      vnode.asyncFactory
    );
    cloned.ns = vnode.ns;
    cloned.isStatic = vnode.isStatic;
    cloned.key = vnode.key;
    cloned.isComment = vnode.isComment;
    cloned.fnContext = vnode.fnContext;
    cloned.fnOptions = vnode.fnOptions;
    cloned.fnScopeId = vnode.fnScopeId;
    cloned.asyncMeta = vnode.asyncMeta;
    cloned.isCloned = true;
    return cloned
  }

注意:cloneVnodeVnode的克隆只是一层浅拷贝,它不会对子节点进行深度克隆。

4.3 Virtual DOM的创建

先简单回顾一下挂载的流程,挂载的过程是调用Vue实例上$mount方法,而$mount的核心是mountComponent函数。如果我们传递的是template模板,模板会先经过编译器的解析,并最终根据不同平台生成对应代码,此时对应的就是将with语句封装好的render函数;如果传递的是render函数,则跳过模板编译过程,直接进入下一个阶段。下一阶段是拿到render

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值