Vue.js 编译模板的过程

Vue.js 编译模板的过程主要分为两个阶段:解析(Parse)和生成(Generate)。
1、解析阶段:这个阶段的主要任务是将模板字符串转换为抽象语法树(AST)。抽象语法树是一种以树状的形式表现源代码结构的模型。在 Vue.js 中,解析器会将模板字符串解析为一棵 AST,每个节点都是一个普通的 JavaScript 对象,这个对象描述了元素/文本节点的各种属性。
2、生成阶段:这个阶段的主要任务是将 AST 转换为渲染函数。渲染函数的主要任务是将模板转换为 Virtual DOM,也就是说,渲染函数的返回值是 Virtual DOM。
这个过程的主要步骤如下:
1、Vue 接收到模板字符串。
2、Vue 使用解析器(Parser)将模板字符串解析为 AST。
3、Vue 使用优化器(Optimizer)标记静态节点。这个步骤不是必须的,但是它可以提高后续的 patch 过程。
4、Vue 使用代码生成器(Code Generator)将 AST 转换为渲染函数。
这个过程是 Vue.js 的编译设计的精髓,它使得 Vue.js 可以提供类似于原生 JavaScript 的性能,同时还能提供一个简单易用的模板语法。
vue-template-compiler是编译vue模板的包,传入模板返回AST抽象语法树。
const compiler = require('vue-template-compiler')const val = compiler.compile('<span class="active" :total="count">666</span>')
输出结果如下:
const res = {ast: {type: 1,tag: 'span',attrsList: [ { name: 'total', value: 'count' } ],attrsMap: { class: 'active', ':total': 'count' },rawAttrsMap: {},parent: undefined,children: [ { type: 3, text: 666, static: true } ],plain: false,staticClass: '"active"',hasBindings: true,attrs: [ { name: 'total', value: 'count', dynamic: false } ],static: false,staticRoot: false},render: `with(this){return _c('span',{staticClass:"active",attrs:{"total":count}},[_v("666")])}`,staticRenderFns: [],errors: [],tips: []}
可以看到对象中有ast属性和render函数,其实ast是为了生成render函数用的。
with (this) {return _c('span',{ staticClass: "active", attrs: { "total": count } },[_v("666")])}
render函数会调用很多辅助的函数,例如 _c,_v 那么这些都来自哪里呢?
其实是渲染时候用的的辅助函数,源码路径 https://github.com/vuejs/vue/blob/dev/src/core/instance/render-helpers/index.js
export function installRenderHelpers (target: any) {target._o = markOncetarget._n = toNumbertarget._s = toStringtarget._l = renderListtarget._t = renderSlottarget._q = looseEqualtarget._i = looseIndexOftarget._m = renderStatictarget._f = resolveFiltertarget._k = checkKeyCodestarget._b = bindObjectPropstarget._v = createTextVNodetarget._e = createEmptyVNodetarget._u = resolveScopedSlotstarget._g = bindObjectListenerstarget._d = bindDynamicKeystarget._p = prependModifier}
发现上面并没有_c,我们继续寻找源码可以发现在 initRender 这里,路径 https://github.com/vuejs/vue/blob/dev/src/core/instance/render.js
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false) 
  createElement 就是创建虚拟节点 VNode。路径 https://github.com/vuejs/vue/blob/dev/src/core/vdom/create-element.js
那么至此,我们大致了解了 createElement 创建 VNode 的过程,每个 VNode 有 children,children 每个元素也是一个 VNode,这样就形成了一个 VNode Tree,它很好的描述了我们的 DOM Tree。
