Vue.js源码分析——模板编译过程

Vue初始化

还是从Vue初始化开始,调试模板是如何进行编译的

<template>
  <div id="app">
    <h1><span>hello</span>Vue</h1>
    <div>{{ message }}</div>
  </div>
</template>
<script>
const vm = new Vue({
  el: '#app',
  data:{
    message: "hello world",
  },
});
</script>

在之前的首次渲染分析中,我们知道vue是通过src/platforms/web/entry-runtime-with-compiler.jscompileToFunctions这个函数,把模板转成render函数的,我们先在compileToFunctions的时候打个断点。接下来进行调试

模板编译流程图

下面的流程就是compileToFunctions的执行过程,可以参照这个流程图进行下面的调试过程

Created with Raphaël 2.2.0 开始 读取缓存中的 CompiledFunctionResult对象,如果有直接返回 调用compile函数编译模板 把字符串形式的js代码转成js方法 缓存并返回res对象(render,staticRenderFns方法) 结束

调试

进入了compileToFunctions函数发现他在src/compiler/to-function.js中定义

export function createCompileToFunctionFn (compile: Function): Function {
/** 1. 使用模板字符串作为key,来缓存中查找是否有编译好的渲染函数,如果有的话直接返回 
*      首次执行还没有缓存渲染函数,这里会继续执行
*/
 const key = options.delimiters
   ? String(options.delimiters) + template
   : template
 if (cache[key]) {
   return cache[key]
 }
/** 2. 调用compile函数开始编译模板 
*      调用compile把 template 编译为对象(render, staticRenderFns), 字符串形式的js代码
*      我们进入compile内部如何进行编译的
*/
 const compiled = compile(template, options)
/** 3. 把字符串形式的js代码转成js方法 */
 function createFunction (code, errors) {
   try {
     return new Function(code)
   } catch (err) {
     errors.push({ err, code })
     return noop
   }
 }  
/** 4. 缓存并返回res对象(render, staticRenderFns方法) */
  return (cache[key] = res)
}
compile

这个函数在src/compiler/create-compiler.js中定义,compile 的核心作用就是合并options,调用baseCompile进行编译,最后记录错误返回编译好的对象

export function createCompilerCreator (baseCompile: Function): Function {
	return function createCompiler (baseOptions: CompilerOptions) {
	  // baseOptions 平台相关的options ,sec/platform/web/compiler/options.js中定义
		function compile (
		  template: string,
		  options?: CompilerOptions
		): CompiledResult {
		  /** 1. 合并baseOptions 和 compile函数的options */
		  const finalOptions = Object.create(baseOptions)
		  ...
		  /** 2. baseCompile 返回的字符串形式的render代码 和 staticRenderFns 
		  *      baseCompile 是编译的核心函数,进入baseCompile看下是如何实现的
		  */
	      const compiled = baseCompile(template.trim(), finalOptions)
		}
	}
}
baseCompile

这个函数在src/compiler/index.js中定义,主要做了三件事

export const createCompiler = createCompilerCreator(function baseCompile (
  template: string,
  options: CompilerOptions
): CompiledResult {
  /** 1. 把模板编译成ast 抽象语法树
  *      抽象语法树,用来以树形方式描述代码结构
  */
  const ast = parse(template.trim(), options)
  
  /** 2. 优化语法树 */
  if (options.optimize !== false) {
    optimize(ast, options)
  }
  /** 3. 把抽象语法树生成字符串形式的 js 代码 */
  const code = generate(ast, options)
 
  return {
    ast,
    // 渲染函数,注意:此时render是字符串形式的
    render: code.render,
    // 静态渲染函数,生成静态 VNode 树
    staticRenderFns: code.staticRenderFns
  }
})

截止到现在主线中字符串形式的代码都已经生成了,下面回到主线该去执行第三步,把字符串形式的代码转成js方法

🦍这里面做个关于抽象语法树的知识点补充

什么是抽象语法树

  • 抽象语法树简称AST
  • 使用对象的形式描述树形的代码结构
  • 此处的抽象语法树是用来形容树形结构的HTML字符串

为什么使用抽象语法树

  • 模板字符串转换成AST后,可以通过AST对模板进行优化
  • 标记模板中的静态内容,在patch的时候直接跳过静态内容
  • 在patch的过程中静态内容不需要对比和重新渲染

总结

模板编译就是把模板字符串转成渲染函数。

  1. 模板编译首先把模板转成ast对象, 然后优化ast对象。优化的过程实际就是在标记静态根节点,
  2. 把优化好的ast对象转成字符串形式代码。
  3. 最终在字符串形式的代码通过new Function转成匿名函数,这个匿名函数就是最终生成的render函数。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值