1. 為什么進(jìn)行模板編譯
在vue中,template中可以寫(xiě)表達(dá)式,條件渲染,循環(huán)渲染,點(diǎn)擊事件等,這些在html中是不可以實(shí)現(xiàn)的,只能在js中實(shí)現(xiàn),所以vue對(duì)他進(jìn)行了處理,處理的方法就是模板編譯為render函數(shù),執(zhí)行render函數(shù),返回vnode
2. 模板編譯過(guò)程:
1. 入口compileToFunctions()函數(shù)——src/platforms/web/entry-runtime-with-compiler.js
2. 進(jìn)入compilerToFunctions()函數(shù),src/compiler/to-function.js中的createCompileToFunctionFn()方法
主要作用:
- 判斷有無(wú)緩存的編譯對(duì)象,如果有直接返回,第一次進(jìn)入時(shí)沒(méi)有緩存
const key = options.delimiters
? String(options.delimiters) + template
: template
// 1. 判斷有無(wú)緩存的編譯對(duì)象compileFunctionResult,如果有直接返回
if (cache[key]) {
return cache[key]
}
- compile把模板編譯為編譯對(duì)象(render,staticRenderFns),字符串形式的js代碼
const compiled = compile(template, options)
- createFunction將字符串形式j(luò)s代碼轉(zhuǎn)換為js方法
res.render = createFunction(compiled.render, fnGenErrors)
res.staticRenderFns = compiled.staticRenderFns.map(code => {
// 把數(shù)組中每一項(xiàng)轉(zhuǎn)為render方法
return createFunction(code, fnGenErrors)
})
- 緩存并返回res對(duì)象
// 4. 緩存并返回res對(duì)象(render,staticRenderFns方法)
return (cache[key] = res)
3. compile主要作用:a. 合并選項(xiàng);b. 調(diào)用baseCompile(template.trim(), finalOptions)
/src/compiler/create-compiler.js——createCompilerCreator()方法
a. 合并選項(xiàng)
// 創(chuàng)建一個(gè)沒(méi)有原型的對(duì)象finalOptions,用來(lái)合入baseOptions和complie傳入的options
const finalOptions = Object.create(baseOptions)
// 編譯過(guò)程中出現(xiàn)的錯(cuò)誤和信息
const errors = []
const tips = []
......
// ?。。aseCompile模板編譯核心函數(shù),把模板編譯成render函數(shù),返回一個(gè)函數(shù)
// 函數(shù)中有兩個(gè)成員,render,staticRenderFns,此時(shí)存儲(chǔ)的是字符串js代碼
const compiled = baseCompile(template.trim(), finalOptions)
b. 調(diào)用baseCompile()方法(src/compiler/index.js——createCompilerCreator(function baseCompile (){...}))
export const createCompiler = createCompilerCreator(function baseCompile (
// 模板,用戶(hù)傳入的參數(shù)
template: string,
options: CompilerOptions
): CompiledResult {
// 將template轉(zhuǎn)為ast抽象語(yǔ)法樹(shù)
// parse傳入兩值,去空格的模板,合入的options
const ast = parse(template.trim(), options)
if (options.optimize !== false) {
// 優(yōu)化抽象語(yǔ)法樹(shù)
optimize(ast, options)
}
// 將抽象語(yǔ)法樹(shù)轉(zhuǎn)為 字符串類(lèi)型 js
const code = generate(ast, options)
return {
// 抽象語(yǔ)法樹(shù)
ast,
// 渲染函數(shù),此時(shí)的render是字符串形式的,還需要調(diào)用toFunction轉(zhuǎn)為js方法
render: code.render,
// 靜態(tài)渲染函數(shù)
staticRenderFns: code.staticRenderFns
}
})
主要有3個(gè)作用:
-
通過(guò)parse將模板編譯為抽象語(yǔ)法樹(shù)ast
- 遍歷html,轉(zhuǎn)換為ast對(duì)象,也就是普通對(duì)象
- 相關(guān)的html指令/屬性記錄在ast相應(yīng)對(duì)象的屬性上
1. ast,抽象語(yǔ)法樹(shù)
抽象語(yǔ)法樹(shù),abstract syntax tree,ast;
使用對(duì)象的形式描述樹(shù)形代碼結(jié)構(gòu);
此處的ast是描述屬性結(jié)構(gòu)的HTML字符串2. 為什么使用抽象語(yǔ)法樹(shù)
模板字符串轉(zhuǎn)換為ast后,可通過(guò)ast對(duì)模板進(jìn)行優(yōu)化
標(biāo)記模板中的靜態(tài)代碼,patch的時(shí)候可跳過(guò)靜態(tài)內(nèi)容
在patch過(guò)程中靜態(tài)代碼不需要對(duì)比和重新渲染
使用babel也是先將代碼轉(zhuǎn)為ast,再將代碼進(jìn)行降級(jí) -
優(yōu)化抽象語(yǔ)法樹(shù)ast,標(biāo)記靜態(tài)節(jié)點(diǎn)與靜態(tài)根節(jié)點(diǎn)
- markStatic標(biāo)記靜態(tài)節(jié)點(diǎn),判斷node.type,=2表達(dá)式不標(biāo)記,=3文本靜態(tài)標(biāo)記;=1標(biāo)簽元素,遍歷子節(jié)點(diǎn)標(biāo)記靜態(tài)節(jié)點(diǎn)
- markStaticRoots標(biāo)記靜態(tài)根節(jié)點(diǎn),靜態(tài)根節(jié)點(diǎn)(標(biāo)簽包含文本,有靜態(tài)子標(biāo)簽;標(biāo)簽只是純文本不是靜態(tài)根節(jié)點(diǎn),因?yàn)閮?yōu)化成本大于收益)
-
generate將抽象語(yǔ)法樹(shù)轉(zhuǎn)為字符串類(lèi)型的js
- 通過(guò)genElement生成字符串的js代碼
- 判斷staticProcessed,用來(lái)標(biāo)記是否被處理,如果已經(jīng)被處理則不處理
- 處理once/if/for轉(zhuǎn)為render指令;template,組件轉(zhuǎn)為字符的js代碼
- genChildren完成后生成了render函數(shù)中所需要的代碼,使用_c傳入tag,data,children
- staticRenderFns靜態(tài)根節(jié)點(diǎn)處理
- 通過(guò)genElement生成字符串的js代碼
4. createFunction將字符串形式j(luò)s代碼轉(zhuǎn)換為js方法
src/compiler/to-function.js——createFunction()
function createFunction (code, errors) {
try {
// 通過(guò)new Function轉(zhuǎn)為js方法
return new Function(code)
} catch (err) {
errors.push({ err, code })
return noop
}
}