Vue.js 编译模板的过程

共 3017字,需浏览 7分钟

 ·

2023-08-22 11:48


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 = markOnce target._n = toNumber target._s = toString target._l = renderList target._t = renderSlot target._q = looseEqual target._i = looseIndexOf target._m = renderStatic target._f = resolveFilter target._k = checkKeyCodes target._b = bindObjectProps target._v = createTextVNode target._e = createEmptyVNode target._u = resolveScopedSlots target._g = bindObjectListeners target._d = bindDynamicKeys target._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。

浏览 90
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报