本文基于vue-2.4.4源碼進(jìn)行分析
模板編譯是Vue 2.0中很重要的一個(gè)環(huán)節(jié),它將template
編譯成render
函數(shù),最后生成Virtual DOM
渲染在頁(yè)面。
本篇文章將結(jié)合源碼對(duì)模板編譯流程進(jìn)行分析:
從源碼角度看,模板編譯主要經(jīng)歷如下流程:
即:
首先,在項(xiàng)目初始化時(shí)掛載DOM節(jié)點(diǎn)
并獲取template
然后將 template
編譯成 render 函數(shù)
。這個(gè)編譯過(guò)程包含:
1. check 緩存,如果有緩存數(shù)據(jù)就讀取緩存數(shù)據(jù)
2. 獲取并合并options
3. parse: 將template解析成AST
4. optimize: 標(biāo)記靜態(tài)節(jié)點(diǎn)
5. generate: 拼接 render function 字符串
6. 通過(guò) new function 生成渲染函數(shù)
7. 緩存
其中,3、4、5是整個(gè)模板編譯的核心。
baseCompile函數(shù)(src/compiler/index.js)依次執(zhí)行parse
,optimize
,和generate
,最后返回一個(gè)包含ast
、render
和staticRenderFns
的對(duì)象。
export const createCompiler = createCompilerCreator(function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult {
// 3. parse: 將template解析成AST
const ast = parse(template.trim(), options)
// 4. optimize: 標(biāo)記靜態(tài)節(jié)點(diǎn)
optimize(ast, options)
// 5. generate: 拼接 render function 字符串
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
parse: 將template解析成AST
這里,先簡(jiǎn)單介紹下AST。
AST全稱是:Abstract Syntax Tree (抽象語(yǔ)法樹),是源代碼語(yǔ)法所對(duì)應(yīng)的樹狀結(jié)構(gòu)。Vue 2.0中ASTNode有三種形式:ASTElement
、ASTText
、ASTExpression
。
Vue 2.0中的parse函數(shù)(src/compiler/parser/index.js)采用了jQuery作者John Resig的HTML Parser
parseHTML(template, {
start (tag, attrs, unary) {
// 解析到新的節(jié)點(diǎn)時(shí)調(diào)用,包括節(jié)點(diǎn)tagName, attributes等信息
},
end () {
// 節(jié)點(diǎn)解析結(jié)束時(shí)調(diào)用,包括節(jié)點(diǎn)tagName等信息
},
chars (text: string) {
// 文本解析完成時(shí)調(diào)用,包括文本本身
}
}
如:<div id='app'>{{ message }}</div>
parseHTML
后的結(jié)果是:
optimize: 標(biāo)記靜態(tài)節(jié)點(diǎn)
optimize函數(shù)(src/compiler/optimizer.js)會(huì)對(duì)靜態(tài)節(jié)點(diǎn)打標(biāo),提取最大的靜態(tài)樹,在后面patch時(shí),被標(biāo)記為static的節(jié)點(diǎn)將直接跳過(guò)diff。
export function optimize (root: ?ASTElement, options: CompilerOptions) {
if (!root) return
isStaticKey = genStaticKeysCached(options.staticKeys || '')
isPlatformReservedTag = options.isReservedTag || no
// first pass: mark all non-static nodes.
markStatic(root)
// second pass: mark static roots.
markStaticRoots(root, false)
}
generate: 拼接 render function 字符串
generate函數(shù)(src/compiler/codegen/index.js)
export function generate (
ast: ASTElement | void,
options: CompilerOptions
): CodegenResult {
const state = new CodegenState(options)
const code = ast ? genElement(ast, state) : '_c("div")'
return {
render: `with(this){return ${code}}`,
staticRenderFns: state.staticRenderFns
}
}
總結(jié)
模板編譯的核心:template
→ AST
→ render function