本文基于vue-2.4.4源碼進行分析
模板編譯是Vue 2.0中很重要的一個環節,它將template
編譯成render
函數,最后生成Virtual DOM
渲染在頁面。
本篇文章將結合源碼對模板編譯流程進行分析:
從源碼角度看,模板編譯主要經歷如下流程:
vue-compile.png
即:
首先,在項目初始化時掛載DOM節點
并獲取template
然后將 template
編譯成 render 函數
。這個編譯過程包含:
1. check 緩存,如果有緩存數據就讀取緩存數據
2. 獲取并合并options
3. parse: 將template解析成AST
4. optimize: 標記靜態節點
5. generate: 拼接 render function 字符串
6. 通過 new function 生成渲染函數
7. 緩存
其中,3、4、5是整個模板編譯的核心。
baseCompile函數(src/compiler/index.js)依次執行parse
,optimize
,和generate
,最后返回一個包含ast
、render
和staticRenderFns
的對象。
export const createCompiler = createCompilerCreator(function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult {
// 3. parse: 將template解析成AST
const ast = parse(template.trim(), options)
// 4. optimize: 標記靜態節點
optimize(ast, options)
// 5. generate: 拼接 render function 字符串
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
parse: 將template解析成AST
這里,先簡單介紹下AST。
AST全稱是:Abstract Syntax Tree (抽象語法樹),是源代碼語法所對應的樹狀結構。Vue 2.0中ASTNode有三種形式:ASTElement
、ASTText
、ASTExpression
。
Vue 2.0中的parse函數(src/compiler/parser/index.js)采用了jQuery作者John Resig的HTML Parser
parseHTML(template, {
start (tag, attrs, unary) {
// 解析到新的節點時調用,包括節點tagName, attributes等信息
},
end () {
// 節點解析結束時調用,包括節點tagName等信息
},
chars (text: string) {
// 文本解析完成時調用,包括文本本身
}
}
如:<div id='app'>{{ message }}</div>
parseHTML
后的結果是:
ast.png
optimize: 標記靜態節點
optimize函數(src/compiler/optimizer.js)會對靜態節點打標,提取最大的靜態樹,在后面patch時,被標記為static的節點將直接跳過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函數(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
}
}
總結
模板編譯的核心:template
→ AST
→ render function
vue-comile-flow.png