vue-compile概述
来源 刘涛
Vue
的核心可以分为三个大块:数据处理和双向绑定、模板编译、虚拟dom。
前面我们对第一部分的主要内容双向绑定做了一个分析讲解,接下来我们说一说模板编译。
这一部分的内容比较多,也比较复杂。由于所涉及的情况太多了,我也不可能把每一种情况都覆盖到。
尽量做到既不啰嗦,又能分享更多的内容。前面我们也提到过,模板编译分为三个阶段:
生成ast
、优化静态内容、生成render
。
官方文档中提到render
比template
更加底层,许多人对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
。包括klass
和style
,对模板中类和样式的解析。directives
。这里包括model
(v-model
)、html
(v-html
)、text
(v-text
)三个指令。isPreTag
。是否是pre
标签。isUnaryTag
。是否是单标签,比如img
、input
、iframe
等。mustUseProp
。需要使用props
绑定的属性,比如value
、selected
等。canBeLeftOpenTag
。可以不闭合的标签,比如tr
、td
等。isReservedTag
。是否是保留标签,html
标签和SVG
标签。getTagNamespace
。获取命名空间,svg
和math
。staticKeys
。静态关键词,包括staticClass,staticStyle
。
上面这些方法或者属性,在编译模板时会用到。
这里知识简单的列出来它们的用途,方便看一眼。
我们来看createCompiler
函数的实现。
export function createCompiler (baseOptions: CompilerOptions) {const functionCompileCache: {
[key: string]: CompiledFunctionResult;
} = Object.create(null)
// compile 函数的实现
// compileToFunctions 函数的实现
return {
compile,
compileToFunctions
}
}
该函数只是compile
和compileToFunctions
的简单封装,开始定义了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
}
我们从上往下,依次看看它都做了哪些事儿。
它接收两个参数template
和options
,template
不用过多解释,
options
在内部主要是用户自己定义的delimiters
。
finalOptions
继承自我们上面提到的baseOptions
,
并添加了一个搜集错误的warn
方法,然后合并了options
传入的各种配置选项。
modules
和directives
合并方法不同是因为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
,并搜集了错误。
compile
和compileToFunctions
两个方法的不同之处有以下几点。
1、 compile
返回的结果中render
是字符串,staticRenderFns
是字符串组成的数组,而compileToFunctions
中把它们变成了函数。
2、 compile
返回的结果中,有模板生成的ast
和搜集到的错误。而compileToFunctions
对其结果进行了一些处理。
以上是 vue-compile概述 的全部内容, 来源链接: utcz.com/z/380188.html