浅谈Vue2.0模板渲染

2019-01-02  本文已影响0人  亱空

Vue 2.0 中模板渲染与 Vue 1.0 完全不同,1.0 中采用的 DocumentFragment,而 2.0 中借鉴 React 的 Virtual DOM。基于 Virtual DOM,2.0 还可以支持服务端渲染(SSR),也支持 JSX 语法。

相关概念介绍

AST (abstract syntax tree)

AST即抽象语法树,是源代码的抽象语法结构的树状表现形式。Vue在mount的过程中,template会被编译成AST语法树。
Vue中的AST数据结构定义如下:

declare type ASTNode = ASTElement | ASTText | ASTExpression 
 
declare type ASTElement = { // 有关元素的一些定义 
 
  type: 1; 
 
  tag: string; 
 
  attrsList: Array{ name: string; value: string }>; 
 
  attrsMap: { [key: string]: string | null }; 
 
  parent: ASTElement | void; 
 
  children: ArrayASTNode>; 
 
  //...... 
 
} 
 
declare type ASTExpression = { 
 
  type: 2; 
 
  expression: string; 
 
  text: string; 
 
  static?: boolean; 
 
} 
 
declare type ASTText = { 
 
  type: 3; 
 
  text: string; 
 
  static?: boolean; 
 
}  

可以看到Vue中的AST数据结构有三种类型,以type区分:

Virtual DOM - 轻量级的模拟DOM结构

VNode - Vue中的VDOM对象

数据结构定义:

constructor { 
 
  this.tag = tag   //元素标签 
 
  this.data = data  //属性 
 
  this.children = children  //子元素列表 
 
  this.text = text 
 
  this.elm = elm  //对应的真实 DOM 元素 
 
  this.ns = undefined 
 
  this.context = context 
 
  this.functionalContext = undefined 
 
  this.key = data && data.key 
 
  this.componentOptions = componentOptions 
 
  this.componentInstance = undefined 
 
  this.parent = undefined 
 
  this.raw = false 
 
  this.isStatic = false //是否被标记为静态节点
 
  this.isRootInsert = true 
 
  this.isComment = false 
 
  this.isCloned = false 
 
  this.isOnce = false 
 
}  

为什么使用VDOM?


模板渲染流程解析

模板渲染流程
模板渲染大致上分为图中的三个阶段,其中$mount()函数是下面所有函数的入口函数,所做的工作主要分为三步:
  1. 如果你的option里面没有自定义render()函数,那么,通过compileToFunctions()将HTML模板编译成可以生成VNode的render函数
  2. new 一个Watcher()实例,触发updateComponent() 方法
  3. 生成VNode,经过patch(),把VNode更新到DOM上

模板编译

模板编译阶段涉及两个函数,compileToFunctions()compile(),其中compileToFunctions()compile()的入口函数,模板编译的结果是生成render函数和staticRenderFns函数的字符串,其中staticRenderFns函数包含被标记为静态节点的 VNode,与后续的diff算法优化相关


在进入compileToFunctions()以后,会先检查缓存中是否有已经编译好的结果,如果有结果则直接从缓存中读取,这样做防止每次同样的模板都要进行重复的编译工作。若无可用缓存,则调用compile(),最后对编译结果进行缓存
compile()做了三件事情,分别是生成AST、优化静态内容、生成render函数,对应三个函数:

最大静态子树:
不包含参数data属性的dom节点,每次data数据改变导致页面重新渲染的时候,最大静态子树不需要重新计算生成

DOM更新

Vue中的DOM更新是一个响应式工作流程,当数据发现变化后,会执行 Watcher 中的update()函数,update() 函数会执行这个渲染函数,输出一个新的 VNode 树形结构的数据。然后再调用patch()函数,拿这个新的 VNode 与旧的 VNode 进行对比,只有发生了变化的节点才会被更新到真实 DOM 树上


patch.js 就是新旧 VNode 对比的 diff 函数,主要是为了优化dom,通过算法使操作dom的行为降到最低,diff 算法来源于 snabbdom,是 VDOM 思想的核心。snabbdom 的算法为了 DOM 操作跨层级增删节点较少的这一目标进行优化,它只会在同层级进行, 不会跨层级比较。

总结

  1. VDOM
    VDOM总损耗 = 虚拟DOM增删改 + diff增删改 + (较少的节点)排版与重绘
    真实DOM总损耗 = 真实DOM完全增删改 + (可能较多的节点)排版与重绘
    目的是为了避免频繁引发大面积的DOM操作,提升性能
  1. 模板编译
    将 template 转换为 AST,优化 AST,再将 AST 转换为 render函数
    目的是为了得到render函数

  2. DOM更新
    render函数通过Watcher与数据产生关联,在数据发生变化时调用patch函数,
    执行此render函数,生成新VNode,与旧VNode进行diff,最终更新DOM树

参考链接
上一篇下一篇

猜你喜欢

热点阅读