Vue源码--深入模板渲染
原文链接:https://geniuspeng.github.io/2018/02/07/vue-compile/
之前整理了vue的响应式原理,在这里有一点是一直很模糊的,就是何时去new一个watcher,当data变化时又如何通知视图去进行怎样的更新…这里面涉及到了模板渲染和虚拟DOM的diff以及更新操作。其实模板渲染过程在实际使用vue的过程可能并不需要太深理解,但就vue来说,这些底层思想可以更好地让我们理解这个框架,以及了解为什么Vue的API要如此设计…
上一次也提过,vue2+与vue1+的模板渲染过程完全不同,vue1使用的是DocumentFragment API,具体就不介绍了(可以直接跳到MDN去了解),而vue2开始则使用了Virtual DOM,基于Virtual DOM,vue2支持了服务端渲染SSR,以及JSX语法。介绍渲染流程之前,先说明两个数据结构:抽象语法树AST,以及VNode。
AST数据结构
AST 的全称是 Abstract Syntax Tree(抽象语法树),是源代码的抽象语法结构的树状表现形式,计算机学科中编译原理的概念。而vue就是将模板代码映射为AST数据结构,进行语法解析。这里采用了flow的语法,flow是一个JS静态类型检查工具。
首先看一下 Vue 2.0 源码中 AST 数据结构 的定义:
1 |
declare type ASTNode = ASTElement | ASTText | ASTExpression |
VNODE数据结构
VNODE就是vue中的虚拟dom节点,VNODE 数据结构 如下:
1 |
constructor ( |
真实DOM存在什么问题,为什么要用虚拟DOM
我们为什么不直接使用原生 DOM 元素,而是使用真实 DOM 元素的简化版 VNode,最大的原因就是 document.createElement 这个方法创建的真实 DOM 元素会带来性能上的损失。我们来看一个 document.createElement 方法的例子
1 |
let div = document.createElement('div');
|
打开 console 运行一下上面的代码,会发现打印出来的属性多达 228 个,而这些属性有 90% 多对我们来说都是无用的。VNode 就是简化版的真实 DOM 元素,关联着真实的dom,比如属性elm,只包括我们需要的属性,并新增了一些在 diff 过程中需要使用的属性,例如 isStatic。
模板渲染流程
先来一张图:
首先从$mount开始,可以看到,mount其实就是拿到了html模板作为template,然后将这个template通过compileToFunctions方法编译成render函数:
1 |
if (template) {
|
那么这个compileToFunctions做了什么呢?主要将 template 编译成 render 函数。首先读缓存,没有缓存就调用 compile 方法拿到 render 函数 的字符串形式,再通过 new Function 的方式生成 render 函数。
1 |
// 有缓存的话就直接在缓存里面拿 |
现在我们具体看一下compile方法,上文中提到 compile 方法就是将 template 编译成 render 函数 的字符串形式。
1 |
export function compile ( |
这个函数主要有三个步骤组成:parse,optimize 和 generate,分别输出一个包含 AST,staticRenderFns 的对象和 render函数 的字符串。
parse 函数,主要功能是将 template字符串解析成 AST,采用了 jQuery 作者 John Resig 的 HTML Parser。前面定义了ASTElement的数据结构,parse 函数就是将template里的结构(指令,属性,标签等)转换为AST形式存进ASTElement中,最后解析生成AST。
optimize 函数(src/compiler/optimizer.js)主要功能就是标记静态节点,为后面 patch 过程中对比新旧 VNode 树形结构做优化。被标记为 static 的节点在后面的 diff 算法中会被直接忽略,不做详细的比较。
generate 函数(src/compiler/codegen/index.js)主要功能就是根据 AST 结构拼接生成 render 函数的字符串。
1 |
export function generate ( |
其中 genElement 函数(src/compiler/codegen/index.js)是会根据 AST 的属性调用不同的方法生成字符串返回。
1 |
export function genElement (el: ASTElement, state: CodegenState): string {
|
以上就是 compile 函数中三个核心步骤的介绍,compile 之后我们得到了 render 函数 的字符串形式,后面通过 new Function 得到真正的渲染函数。数据发现变化后,会执行 Watcher 中的 _update 函数(src/core/instance/lifecycle.js),_update 函数会执行这个渲染函数,输出一个新的 VNode 树形结构的数据。然后在调用 patch 函数,拿这个新的 VNode 与旧的 VNode 进行对比,只有发生了变化的节点才会被更新到真实 DOM 树上。
mount后续
通过compile生成render方法之后,会进一步执行mount方法,在$mount中可以看到最后一句话:return mount.call(this, el, hydrating),这个mount实际上就是runtime中的mount,执行的就是lifecycle中的mountComponent方法,看一下基本逻辑:
1 |
// 触发 beforeMount 生命周期钩子 |
首先会new一个watcher对象(主要是将模板与数据建立联系),在watcher对象创建后,会运行传入的方法 vm._update(vm._render(), hydrating) 。其中的vm._render()主要作用就是运行前面compiler生成的render方法,并返回一个vNode对象。vm.update() 则会对比新的 vdom 和当前 vdom,并把差异的部分渲染到真正的 DOM 树上。
patch
patch.js 就是新旧 VNode 对比的 diff 函数,主要是为了优化dom,通过算法使操作dom的行为降到最低,diff 算法来源于 snabbdom,是 VDOM 思想的核心。snabbdom 的算法为了 DOM 操作跨层级增删节点较少的这一目标进行优化,它只会在同层级进行, 不会跨层级比较。
文末福利:
福利一:前端,Java,产品经理,微信小程序,Python等10G资源合集大放送:https://www.jianshu.com/p/e8197d4d9880
福利二:微信小程序入门与实战全套详细视频教程。
【领取方法】
关注 【编程微刊】微信公众号:
回复【小程序demo】一键领取130个微信小程序源码demo资源。
回复【领取资源】一键领取前端,Java,产品经理,微信小程序,Python等资源合集10G资源大放送。
Vue源码--深入模板渲染的更多相关文章
- Vue源码翻译之渲染逻辑链
本篇文章主要要记录说明的是,Vue在Vdom的创建上的相关细节.这也是描绘了Vue在界面的创建上的一个逻辑顺序,同时我也非常拜服作者编码的逻辑性,当然或许这么庞大复杂的编码不是一次性铸就的,我想应该也 ...
- vue源码分析—模板解析
福建省啦剑飞傻了剑飞撒到了看风景啊撒:
- Vue源码探究-虚拟DOM的渲染
Vue源码探究-虚拟DOM的渲染 在虚拟节点的实现一篇中,除了知道了 VNode 类的实现之外,还简要地整理了一下DOM渲染的路径.在这一篇中,主要来分析一下两条路径的具体实现代码. 按照创建 Vue ...
- [Vue源码]一起来学Vue模板编译原理(一)-Template生成AST
本文我们一起通过学习Vue模板编译原理(一)-Template生成AST来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学Vu ...
- [Vue源码]一起来学Vue模板编译原理(二)-AST生成Render字符串
本文我们一起通过学习Vue模板编译原理(二)-AST生成Render字符串来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学V ...
- Vue 源码解读(10)—— 编译器 之 生成渲染函数
前言 这篇文章是 Vue 编译器的最后一部分,前两部分分别是:Vue 源码解读(8)-- 编译器 之 解析.Vue 源码解读(9)-- 编译器 之 优化. 从 HTML 模版字符串开始,解析所有标签以 ...
- 大白话Vue源码系列(01):万事开头难
阅读目录 Vue 的源码目录结构 预备知识 先捡软的捏 Angular 是 Google 亲儿子,React 是 Facebook 小正太,那咱为啥偏偏选择了 Vue 下手,一句话,Vue 是咱见过的 ...
- 大白话Vue源码系列(03):生成render函数
阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...
- 大白话Vue源码系列(04):生成render函数
阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...
随机推荐
- 洛谷 P1405 苦恼的小明
P1405 苦恼的小明 题目描述 黄小明和他的合伙人想要创办一所英语培训机构,注册的时候要填一张个人情况的表格,在身高一栏小明犯了愁. 身高要求精确到厘米,但小明实在太高了,无法在纸上填下这么长的数字 ...
- 微软推送Win10致全球网络负担增大,中国网友表示毫无压力
7月29日晚间,微软的Windows10操作系统正式上市发行.微软将面向全球190个国家地区进行windows10的免费升级推送. 眼下, Windows 10 正以疯狂的速度登陆全球各类设备 ...
- python核心编程五——映像和集合
1.字典 不同意一个键相应多个值:当有键发生冲突(即.字典键反复赋值),取最后(近期)的赋值. >>> dict1 = {' foo':789, 'foo': 'xyz'} ...
- 27.Node.js模块系统
转自:http://www.runoob.com/nodejs/nodejs-module-system.html 为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统. 模 ...
- 为您的Office文档加把锁-ADRMS的安装
为您的Office文档加把锁-ADRMS的安装 如今不少企业越来越重视自己KM(知识管理系统)的建立对于KM的建立实施虽然可以有效地解决企业在知识管理上的问题对于一些具有商业利益关系的机密文件(例如: ...
- Spring学习总结(8)——25个经典的Spring面试问答
1.什么是Spring框架?Spring框架有哪些主要模块? Spring框架是一个为Java应用程序的开发提供了综合.广泛的基础性支持的Java平台.Spring帮助开发者解决了开发中基础性的问题, ...
- JavaScript学习总结(4)——JavaScript数组
JavaScript中的Array对象就是数组,首先是一个动态数组,无需预先制定大小,而且是一个像Java中数组.ArrayList.Hashtable等的超强综合体. 一.数组的声明 常规方式声明: ...
- Linux中删除文件,磁盘空间未释放问题追踪
在客户使用我们产品后,发现一个问题:在删除了文件后.磁盘空间却没有释放.是有进程在打开这个文件,还是其它情况?我们一起来看看一下两个场景 一. 场景一:进程打开此文件 当一个文件正在被一个进程使用时. ...
- Windows Route 路由表命令整理
Windows Route 路由表命令 在本地 IP 路由表中显示和修改条目. 语法 route [-f] [-p] [Command [Destination] [mask Netmask] [Ga ...
- 1.2 Use Cases中 Stream Processing官网剖析(博主推荐)
不多说,直接上干货! 一切来源于官网 http://kafka.apache.org/documentation/ Stream Processing 流处理 Many users of Kafka ...