Vue模版编译

什么是vue模板编译

在使用vue开发过程中,我们把写在<template></template>标签中的内容称之为模板。除去一些html原生的内容还有soltv-ifv-on{{}}这些原生html不存在的语法,但是仍然可别浏览器识别,其中最重要的一个原因就是vue的模板编译了。

Vue会把用户在<template> </template>标签中写的内容进行编译,找出原生的html和非原生的html内容,经过处理生成渲染函数,也就是render函数。render函数会将模板内容生成对应的VNode,而VNode会经过patch过程从而得到将渲染的视图中的VNode,最后根据VNode创建真实的DOM节点并插入到视图中,最终完成视图的渲染更新。

而用户在<template></template>标签中写的类似于原生html的内容进行编译,生成渲染函数这一过程就是模版编译过程。

编译入口

模板编译过程是在vue实例初始化过程中到了mount过程开始的。在initMixin函数中对vue原型上添加了_init函数,在_init函数中进行完生命周期初始化、时间机制初始化、数据初始化等,会调用created钩子函数,然后会判断是否有el,如果有el则执行vue原型上的$mount,进行挂载。模板编译的过程也就在$mount中,重点在$mount内部执行了那些代码。$mount核心源码如下:

//src/platforms/web/entry-runtime-with-compiler.js 
// 省略了部分代码,只保留了关键部分
import { compileToFunctions } from './compiler/index'

const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (el) {
  const options = this.$options
  
  // 如果没有 render 方法,则进行 template 编译
  if (!options.render) {
    let template = options.template
    if (template) {
      // 调用 compileToFunctions,编译 template,得到 render 方法
      const { render, staticRenderFns } = compileToFunctions(template, {
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      // 这里的 render 方法就是生成生成虚拟 DOM 的方法
      options.render = render
    }
  }
  return mount.call(this, el, hydrating)
}

我们发现主要的编译函数在compileToFunctions,那么这个函数是从哪来的呢,点进去发现

//src/platforms/web/compiler/index.js 
/* @flow */

import { baseOptions } from './options'
import { createCompiler } from 'compiler/index'

const { compile, compileToFunctions } = createCompiler(baseOptions)

export { compile, compileToFunctions }

继续追溯,createCompiler函数是从src/compiler/index.js引入的,核心源码如下

export const createCompiler = createCompilerCreator(function baseCompile (
  template: string,
  options: CompilerOptions
): CompiledResult {
  // 模板解析阶段:用正则等方式解析template模板中的指令、class、style等数据,形成AST
  const ast = parse(template.trim(), options)
  if (options.optimize !== false) {
    // 优化阶段:遍历AST,找出其中的静态节点,并打上标记
    optimize(ast, options)
  }
  // render函数生成阶段:将AST转换成渲染函数
  const code = generate(ast, options)
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})

这里就到了模板编译的入口了。

模板编译内部流程

那么把<template></template>内容编译成render函数过程中都发生了什么呢?

抽象语法树AST

什么是抽象语法树:抽象语法和抽象语法树其实就是源代码的抽象语法结构和属性表现形式。

我们常用的浏览器就是通过将js代码转化为抽象语法树来进行下一步的分析等其他操作。

如图

可以理解为根据一定规则,将固定的语法转化为指定的js对象,在根据对象做后续操作。

vue模板编译的第一步就是将模板字符串根据正则等判断生成AST语法树。

具体流程

将模板字符串解析成抽象语法树AST后,我们就可以对其进行后续操作,也就是优化阶段,用优化后的AST来生成render函数,核心代码分为三个阶段

  1. 模板解析阶段——解析器(parse()) ——源码路径:src/compiler/parser/index.js
  2. 优化阶段——优化器(optimize())——源码路径:src/compiler/optimizer.js
  3. 代码生成阶段——代码生成器——源码路径:src/compiler/codegen/index.js

整个流程是在src/complier/index.js中的basCompile函数中完成的。具体源码如下:

export const createCompiler = createCompilerCreator(function baseCompile (
  template: string,
  options: CompilerOptions
): CompiledResult {
  // 模板解析阶段:用正则等方式解析template模板中的指令、class、style等数据,形成AST
  const ast = parse(template.trim(), options)
  if (options.optimize !== false) {
    // 优化阶段:遍历AST,找出其中的静态节点,并打上标记
    optimize(ast, options)
  }
  // render函数生成阶段:将AST转换成渲染函数
  const code = generate(ast, options)
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})
  1. const ast = parse(template.trim(), options):parse主要功能就是用正则等方式将模板字符串解析成AST
  2. optimize(ast, options):optimize的主要作用是标记静态节点,这是Vue在编译过程中的优化,在patch过程中,DOM-Diff算法会直接跳过静态节点,从而减少来比较的过程,优化了patch的性能
  3. const code = generate(ast, options):将AST转化成render函数字符串的过程,得到一个render函数的字符串以及staticRenderFns字符串

后面会整理详细的parse、optimize、generate逻辑。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值