浅谈Vue2.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区分:

  • ASTElement 标签
  • ASTExpression 包含字面量表达式的文本节点
  • ASTText 普通文本节点或注释节点

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

  • 结构为有序的二叉树
  • 与真实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?

  • 创建真实DOM的代价高
    真实的DOM节点实现的属性很多,仅一个<div>就有多达200种属性,而VNode仅仅实现一些必要的属性,相比起来,创建一个VNode的成本比较低
  • 真实DOM频繁排版与重绘的效率是相当低的
  • 虚拟DOM进行频繁修改,然后一次性比较并修改真实DOM中需要改的部分

模板渲染流程解析

模板渲染流程

模板渲染大致上分为图中的三个阶段,其中$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函数,对应三个函数:

  • parse ()
    主要功能是将 template字符串解析成 AST,采用了 jQuery 作者 John Resig 的 HTML Parser。前面定义了ASTElement的数据结构,parse 函数就是将template里的结构(指令,属性,标签等)转换为AST形式存进ASTElement中,最后解析生成AST

  • optimize()
    主要功能就是标记静态节点,获得AST对象的最大静态子树,为后面 patch 过程中对比新旧 VNode 树形结构做优化。被标记为 static 的节点在后面的 diff 算法中会被直接忽略,不做详细的比较

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

  • generate()
    根据 AST 结构拼接生成 render 函数的字符串,另外还会生成staticRenderFns函数字符串

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树

参考链接
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容

  • vue2.0和1.0模板渲染的区别 Vue 2.0 中模板渲染与 Vue 1.0 完全不同,1.0 中采用的 Do...
    我是上帝可爱多阅读 1,288评论 0 4
  • 前几天想学学Vue中怎么编写可复用的组件,提到要对Vue的render函数有所了解。可仔细一想,对于Vue的ren...
    kangaroo_v阅读 116,012评论 13 171
  • 回忆 首先,render函数中手写h=>h(app),new Vue()实例初始化init()和原来一样。$mou...
    LoveBugs_King阅读 2,270评论 1 2
  • 1.vue data属性里面的getter和setter data的每个属性都有两个相对应的get和set属性。 ...
    GXW_Lyon阅读 676评论 0 0
  • 回忆 这里我们将对render函数把template转化成vnode的过程进行介绍。 Vue.prototype....
    LoveBugs_King阅读 926评论 0 0