vue-compile概述

来源 刘涛

Vue的核心可以分为三个大块:数据处理和双向绑定模板编译虚拟dom

前面我们对第一部分的主要内容双向绑定做了一个分析讲解,接下来我们说一说模板编译。

这一部分的内容比较多,也比较复杂。由于所涉及的情况太多了,我也不可能把每一种情况都覆盖到。

 

尽量做到既不啰嗦,又能分享更多的内容。前面我们也提到过,模板编译分为三个阶段:

生成ast优化静态内容生成render

 

官方文档中提到rendertemplate更加底层,许多人对render函数都不是很明白,

相信通过这一部分的分析,你可以对render基本做到了如指掌

如果你在创建对象时直接传入render函数,模板编译这一步就可以直接跳过,这样效率肯定更高,

 

(react就是不用模板编译的, 如果vue比react效率高,那就是因为需要编译jsx?

 

但同时我们编写代码的难度会增加很多。实际开发过程中,根据需要,恰当选择。

 

Vue对象上有一个全局函数compile,在src/entries/web-runtime-with-compiler.js中。

import { compileToFunctions } from 'web/compiler/index' ... ... ... Vue.compile = compileToFunctions

该方法来自src/platforms/web/compiler/index文件。

import { isUnaryTag, canBeLeftOpenTag } from './util' import { genStaticKeys } from 'shared/util' import { createCompiler } from 'compiler/index' import modules from './modules/index' import directives from './directives/index' import { isPreTag, mustUseProp, isReservedTag, getTagNamespace } from '../util/index' export const baseOptions: CompilerOptions = { expectHTML: true, modules, directives, isPreTag, isUnaryTag, mustUseProp, canBeLeftOpenTag, isReservedTag, getTagNamespace, staticKeys: genStaticKeys(modules) } const { compile, compileToFunctions } = createCompiler(baseOptions) export { compile, compileToFunctions }

该文件中主要定义了一个baseOptions,它主要保存了解析模板时和平台相关的一些配置

对应的src/platforms/weex/compiler/index中也有一份名称一样的配置。

这里我们简单说说web相关的这些配置都是什么意思。

 

  • expectHTML。目前具体还不是很明白,weex中没有改项,从字面意思来看,应该是是否期望HTML

  • modules。包括klassstyle,对模板中类和样式的解析。

  • directives。这里包括modelv-model)、htmlv-html)、text(v-text)三个指令。

  • isPreTag。是否是pre标签。

  • isUnaryTag。是否是单标签,比如imginputiframe等。

  • mustUseProp。需要使用props绑定的属性,比如valueselected等。

  • canBeLeftOpenTag。可以不闭合的标签,比如trtd等。

  • isReservedTag。是否是保留标签html标签和SVG标签。

  • getTagNamespace。获取命名空间svgmath

  • staticKeys。静态关键词,包括staticClass,staticStyle

上面这些方法或者属性,在编译模板时会用到。

这里知识简单的列出来它们的用途,方便看一眼。

我们来看createCompiler函数的实现。

 

export function createCompiler (baseOptions: CompilerOptions) { const functionCompileCache: { [key: string]: CompiledFunctionResult; } = Object.create(null) // compile 函数的实现 // compileToFunctions 函数的实现 return { compile, compileToFunctions } }

该函数只是compilecompileToFunctions的简单封装,开始定义了functionCompileCache

它用来缓存编译之后的模板,方便之后复用

因为compileToFunctions里面调用了compile,所以我们先看一下compile

 

  function compile (
    template: string,
    options?: CompilerOptions ): CompiledResult { const finalOptions = Object.create(baseOptions) const errors = [] const tips = [] finalOptions.warn = (msg, tip) => { (tip ? tips : errors).push(msg) } if (options) { // merge custom modules if (options.modules) { finalOptions.modules = (baseOptions.modules || []).concat(options.modules) } // merge custom directives if (options.directives) { finalOptions.directives = extend( Object.create(baseOptions.directives), options.directives ) } // copy other options for (const key in options) { if (key !== 'modules' && key !== 'directives') { finalOptions[key] = options[key] } } } const compiled = baseCompile(template, finalOptions) if (process.env.NODE_ENV !== 'production') { errors.push.apply(errors, detectErrors(compiled.ast)) } compiled.errors = errors compiled.tips = tips return compiled }

我们从上往下,依次看看它都做了哪些事儿。

它接收两个参数templateoptionstemplate不用过多解释,

options在内部主要是用户自己定义的delimiters

 

finalOptions继承自我们上面提到的baseOptions

并添加了一个搜集错误的warn方法,然后合并了options传入的各种配置选项。

modulesdirectives合并方法不同是因为modules是数组,而directives是一个对象。

 

baseCompile中执行的就是模板编译的三个重要步骤,后面我们会详细讲解。

 

最终,返回编译之后的对象。

 

function compileToFunctions (
    template: string,
    options?: CompilerOptions, vm?: Component ): CompiledFunctionResult { options = options || {} ... // check cache const key = options.delimiters ? String(options.delimiters) + template : template if (functionCompileCache[key]) { return functionCompileCache[key] } // compile const compiled = compile(template, options) // check compilation errors/tips if (process.env.NODE_ENV !== 'production') { if (compiled.errors && compiled.errors.length) { warn( `Error compiling template:\n\n${template}\n\n` + compiled.errors.map(e => `- ${e}`).join('\n') + '\n', vm ) } if (compiled.tips && compiled.tips.length) { compiled.tips.forEach(msg => tip(msg, vm)) } } // turn code into functions const res = {} const fnGenErrors = [] res.render = makeFunction(compiled.render, fnGenErrors) const l = compiled.staticRenderFns.length res.staticRenderFns = new Array(l)
for (let i = 0; i < l; i++) { res.staticRenderFns[i] = makeFunction(compiled.staticRenderFns[i], fnGenErrors) } if (process.env.NODE_ENV !== 'production') {
if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { warn( `Failed to generate render function:\n\n` + fnGenErrors.map(({ err, code }) => `${err.toString()} in\n\n${code}\n`).join('\n'), vm ) } } return (functionCompileCache[key] = res) }

compileToFunctions函数中,首先从缓存中获取编译结果,没有则调用compile函数来编译。

在开发环境,我们在这里会抛出编译过程中产生的错误,

最终返回一个含有render函数,和staticRenderFns数组的对象,并把它放在缓存中。

 

这里我们使用makeFunction来创建函数。

function makeFunction (code, errors) {
  try {
    return new Function(code) } catch (err) { errors.push({ err, code }) return noop } }

很简单,就是利用了我们new Function,并搜集了错误。

compilecompileToFunctions两个方法的不同之处有以下几点。

1、 compile返回的结果中render是字符串,staticRenderFns是字符串组成的数组,而compileToFunctions中把它们变成了函数。

2、 compile返回的结果中,有模板生成的ast和搜集到的错误。而compileToFunctions对其结果进行了一些处理。

转载于:https://www.cnblogs.com/dhsz/p/7685056.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值