前兩篇主要了解了 vue 編譯器的 解析 和 優化:
下面就瞭解一下 vue 編譯器是如何從 AST 語法樹 生成執行渲染函數.
createCompiler() 方法 —— 入口
檔案位置:/src/compiler/index.js
其中最主要的就是 generate(ast, options)
方法,它負責從 AST
語法樹生成渲染函數.
/* 在這之前做的所有的事情,只是為了構建平臺特有的編譯選項(options),比如 web 平臺 1、將 html 模版解析成 ast 2、對 ast 樹進行靜態標記 3、將 ast 生成渲染函數 - 靜態渲染函數放到 code.staticRenderFns 陣列中 - 動態渲染函數 code.render - 在將來渲染時執行渲染函數能夠得到 vnode */ export const createCompiler = createCompilerCreator(function baseCompile( template: string, options: CompilerOptions ): CompiledResult { /* 將模版字串解析為 AST 語法樹 每個節點的 ast 物件上都設定了元素的所有資訊,如,標籤資訊、屬性資訊、插槽資訊、父節點、子節點等 */ const ast = parse(template.trim(), options) /* 優化,遍歷 AST,為每個節點做靜態標記 - 標記每個節點是否為靜態節點,保證在後續更新中跳過這些靜態節點 - 標記出靜態根節點,用於生成渲染函數階段,生成靜態根節點的渲染函數 優化,遍歷 AST,為每個節點做靜態標記 */ if (options.optimize !== false) { optimize(ast, options) } /* 從 AST 語法樹生成渲染函數 如:code.render = "_c('div',{attrs:{"id":"app"}},_l((arr),function(item){return _c('div',{key:item},[_v(_s(item))])}),0)" */ const code = generate(ast, options) return { ast, render: code.render, staticRenderFns: code.staticRenderFns } })
generate() 方法
檔案位置:src\compiler\codegen\index.js
其中在給 code
賦值時,主要的內容是通過 genElement(ast, state)
方法進行生成的.
/* 從 AST 生成渲染函數: - render 為字串的程式碼 - staticRenderFns 為包含多個字串的程式碼,形式為 `with(this){return xxx}` */ export function generate ( ast: ASTElement | void, // ast 物件 options: CompilerOptions // 編譯選項 ): CodegenResult { /* 範例化 CodegenState 物件,引數是編譯選項,最終得到 state ,其中大部分屬性和 options 一樣 */ const state = new CodegenState(options) /* 生成字串格式的程式碼,比如:'_c(tag, data, children, normalizationType)' - data 為節點上的屬性組成 JSON 字串,比如 '{ key: xx, ref: xx, ... }' - children 為所有子節點的字串格式的程式碼組成的字串陣列,格式: `['_c(tag, data, children)', ...],normalizationType`, - normalization 是 _c 的第四個引數,表示節點的規範化型別(非重點,可跳過) 注意:code 並不一定就是 _c,也有可能是其它的,比如整個元件都是靜態的,則結果就為 _m(0) */ const code = ast ? (ast.tag === 'script' ? 'null' : genElement(ast, state)) : '_c("div")' return { render: `with(this){return ${code}}`, staticRenderFns: state.staticRenderFns } }
genElement() 方法
檔案位置:src\compiler\codegen\index.js
export function genElement (el: ASTElement, state: CodegenState): string { if (el.parent) { el.pre = el.pre || el.parent.pre } if (el.staticRoot && !el.staticProcessed) { /* 處理靜態根節點,生成節點的渲染函數 1、將當前靜態節點的渲染函數放到 staticRenderFns 陣列中 2、返回一個可執行函數 _m(idx, true or '') */ return genStatic(el, state) } else if (el.once && !el.onceProcessed) { /* 處理帶有 v-once 指令的節點,結果會有三種: 1、當前節點存在 v-if 指令,得到一個三元表示式,`condition ? render1 : render2` 2、當前節點是一個包含在 v-for 指令內部的靜態節點,得到 `_o(_c(tag, data, children), number, key)` 3、當前節點就是一個單純的 v-once 節點,得到 `_m(idx, true of '')` */ return genOnce(el, state) } else if (el.for && !el.forProcessed) { /* 處理節點上的 v-for 指令,得到: `_l(exp, function(alias, iterator1, iterator2){return _c(tag, data, children)})` */ return genFor(el, state) } else if (el.if && !el.ifProcessed) { /* 處理帶有 v-if 指令的節點,最終得到一個三元表示式:`condition ? render1 : render2` */ return genIf(el, state) } else if (el.tag === 'template' && !el.slotTarget && !state.pre) { /* 當前節點是 template 標籤也不是 插槽 和 帶有 v-pre 指令的節點時走這裡 生成所有子節點的渲染函數,返回一個陣列,格式如: `[_c(tag, data, children, normalizationType), ...]` */ return genChildren(el, state) || 'void 0' } else if (el.tag === 'slot') { /* 生成插槽的渲染函數,得到: `_t(slotName, children, attrs, bind)` */ return genSlot(el, state) } else { /* component or element 處理 動態元件 和 普通元素(自定義元件、原生標籤、平臺保留標籤,如 web 平臺中的每個 html 標籤) */ let code if (el.component) { /* 處理動態元件,生成動態元件的渲染函數,得到 `_c(compName, data, children)` */ code = genComponent(el.component, el, state) } else { // 處理普通元素(自定義元件、原生標籤) let data if (!el.plain || (el.pre && state.maybeComponent(el))) { /* 非普通元素或者帶有 v-pre 指令的元件走這裡,處理節點的所有屬性,返回一個 JSON 字串, 比如: '{ key: xx, ref: xx, ... }' */ data = genData(el, state) } /* 處理子節點,得到所有子節點字串格式的程式碼組成的陣列,格式: `['_c(tag, data, children)', ...],normalizationType` 其中的 normalization 表示節點的規範化型別(非重點,可跳過) */ const children = el.inlineTemplate ? null : genChildren(el, state, true) /* 得到最終的字串格式的程式碼,格式:_c(tag, data, children, normalizationType) */ code = `_c('${el.tag}'${ data ? `,${data}` : '' // data }${ children ? `,${children}` : '' // children })` } /* 如果提供了 transformCode 方法,則最終的 code 會經過各個模組(module)的該方法處理, 不過框架沒提供這個方法,不過即使處理了,最終的格式也是 _c(tag, data, children) module transforms */ for (let i = 0; i < state.transforms.length; i++) { code = state.transforms[i](el, code) } // 返回 code return code } }
genChildren() 方法
檔案位置:src\compiler\codegen\index.js
/* 生成所有子節點的渲染函數,返回一個陣列,格式如: `[_c(tag, data, children, normalizationType), ...]` */ export function genChildren ( el: ASTElement, state: CodegenState, checkSkip?: boolean, altGenElement?: Function, altGenNode?: Function ): string | void { // 獲取所有子節點 const children = el.children if (children.length) { // 第一個子節點 const el: any = children[0] // optimize single v-for if (children.length === 1 && el.for && el.tag !== 'template' && el.tag !== 'slot' ) { /* 優化處理: - 條件:只有一個子節點 && 子節點的上有 v-for 指令 && 子節點的標籤不為 template 或者 slot - 方式:直接呼叫 genElement 生成該節點的渲染函數,不需要走下面的迴圈然後呼叫 genCode 最後得到渲染函數 */ const normalizationType = checkSkip ? state.maybeComponent(el) ? `,1` : `,0` : `` return `${(altGenElement || genElement)(el, state)}${normalizationType}` } // 獲取節點規範化型別,返回一個 number: 0、1、2(非重點,可跳過) const normalizationType = checkSkip ? getNormalizationType(children, state.maybeComponent) : 0 // 是一個函數,負責生成程式碼的一個函數 const gen = altGenNode || genNode /* 返回一個陣列,其中每個元素都是一個子節點的渲染函數 格式:['_c(tag, data, children, normalizationType)', ...] */ return `[${children.map(c => gen(c, state)).join(',')}]${ normalizationType ? `,${normalizationType}` : '' }` } }
genNode() 方法
檔案位置:src\compiler\codegen\index.js
function genNode (node: ASTNode, state: CodegenState): string { // 處理普通元素節點 if (node.type === 1) { return genElement(node, state) } else if (node.type === 3 && node.isComment) { // 處理文字註釋節點 return genComment(node) } else { // 處理文位元組點 return genText(node) } }
genComment() 方法
檔案位置:src\compiler\codegen\index.js
// 得到返回值,格式為:`_e(xxxx)` export function genComment (comment: ASTText): string { return `_e(${JSON.stringify(comment.text)})` }
genText() 方法
檔案位置:src\compiler\codegen\index.js
// 得到返回值,格式為:`_v(xxxxx)` export function genText (text: ASTText | ASTExpression): string { return `_v(${text.type === 2 ? text.expression // no need for () because already wrapped in _s() : transformSpecialNewlines(JSON.stringify(text.text)) })` }
genData() 方法
檔案位置:src\compiler\codegen\index.js
/* 處理節點上的眾多屬性,最後生成這些屬性組成的 JSON 字串, 比如 data = { key: xx, ref: xx, ... } */ export function genData(el: ASTElement, state: CodegenState): string { // 節點的屬性組成的 JSON 字串 let data = '{' /* 首先先處理指令,因為指令可能在生成其它屬性之前改變這些屬性 執行指令編譯方法,如 web 平臺的 v-text、v-html、v-model,然後在 el 物件上新增相應的屬性, 如 v-text:el.textContent = _s(value, dir) v-html:el.innerHTML = _s(value, dir) 當指令在執行時還有任務時,比如 v-model, 則返回 directives: [{ name, rawName, value, arg, modifiers }, ...}] */ const dirs = genDirectives(el, state) if (dirs) data += dirs + ',' // key,data = { key: xxx } if (el.key) { data += `key:${el.key},` } // ref,data = { ref: xxx } if (el.ref) { data += `ref:${el.ref},` } // 帶有 ref 屬性的節點在帶有 v-for 指令的節點的內部,data = { refInFor: true } if (el.refInFor) { data += `refInFor:true,` } // pre,v-pre 指令,data = { pre: true } if (el.pre) { data += `pre:true,` } // 動態元件 <component is="xxx">,data = { tag: 'component' } if (el.component) { data += `tag:"${el.tag}",` } /* 為節點執行模組 (class、style) 的 genData 方法, 得到 data = { staticClass: xx, class: xx, staticStyle: xx, style: xx } module data generation functions */ for (let i = 0; i < state.dataGenFns.length; i++) { data += state.dataGenFns[i](el) } /* 其它屬性,得到 data = { attrs: 靜態屬性字串 } 或者 data = { attrs: '_d(靜態屬性字串, 動態屬性字串)' } attributes */ if (el.attrs) { data += `attrs:${genProps(el.attrs)},` } // DOM props,結果 el.attrs 相同 if (el.props) { data += `domProps:${genProps(el.props)},` } /* 自定義事件 - data = { `on${eventName}:handleCode` } 或者 - { `on_d(${eventName}:handleCode`, `${eventName},handleCode`) } event handlers */ if (el.events) { data += `${genHandlers(el.events, false)},` } /* 帶 .native 修飾符的事件, - data = { `nativeOn${eventName}:handleCode` } 或者 - { `nativeOn_d(${eventName}:handleCode`, `${eventName},handleCode`) */ if (el.nativeEvents) { data += `${genHandlers(el.nativeEvents, true)},` } /* 非作用域插槽,得到 data = { slot: slotName } slot target only for non-scoped slots */ if (el.slotTarget && !el.slotScope) { data += `slot:${el.slotTarget},` } // scoped slots,作用域插槽,data = { scopedSlots: '_u(xxx)' } if (el.scopedSlots) { data += `${genScopedSlots(el, el.scopedSlots, state)},` } /* 處理 v-model 屬性,得到 data = { model: { value, callback, expression } } component v-model */ if (el.model) { data += `model:{value:${el.model.value },callback:${el.model.callback },expression:${el.model.expression }},` } /* inline-template,處理內聯模版,得到: data = { inlineTemplate: { render: function() { render 函數 }, staticRenderFns: [ function() {}, ... ] } } */ if (el.inlineTemplate) { const inlineTemplate = genInlineTemplate(el, state) if (inlineTemplate) { data += `${inlineTemplate},` } } // 刪掉 JSON 字串最後的 逗號,然後加上閉合括號 } data = data.replace(/,$/, '') + '}' /* v-bind 動態引數包裝 必須使用相同的 v-bind 物件應用動態繫結引數 合併輔助物件,以便正確處理 class/style/mustUseProp 屬性。 */ if (el.dynamicAttrs) { data = `_b(${data},"${el.tag}",${genProps(el.dynamicAttrs)})` } // v-bind data wrap if (el.wrapData) { data = el.wrapData(data) } // v-on data wrap if (el.wrapListeners) { data = el.wrapListeners(data) } return data }
genDirectives() 方法
檔案位置:src\compiler\codegen\index.js
/** 執行指令的編譯方法,如果指令存在執行時任務,則返回 directives: [{ name, rawName, value, arg, modifiers }, ...}] */ function genDirectives(el: ASTElement, state: CodegenState): string | void { // 獲取指令陣列 const dirs = el.directives // 不存在指令,直接結束 if (!dirs) return // 指令的處理結果 let res = 'directives:[' // 用於標記指令是否需要在執行時完成的任務,比如 v-model 的 input 事件 let hasRuntime = false let i, l, dir, needRuntime // 遍歷指令陣列 for (i = 0, l = dirs.length; i < l; i++) { dir = dirs[i] needRuntime = true // 獲取節點當前指令的處理方法,比如 web 平臺的 v-html、v-text、v-model const gen: DirectiveFunction = state.directives[dir.name] if (gen) { // 執行指令的編譯方法,如果指令還需要執行時完成一部分任務,則返回 true,比如 v-model needRuntime = !!gen(el, dir, state.warn) } if (needRuntime) { // 表示該指令在執行時還有任務 hasRuntime = true // res = directives:[{ name, rawName, value, arg, modifiers }, ...] res += `{name:"${dir.name}",rawName:"${dir.rawName}"${dir.value ? `,value:(${dir.value}),expression:${JSON.stringify(dir.value)}` : '' }${dir.arg ? `,arg:${dir.isDynamicArg ? dir.arg : `"${dir.arg}"`}` : '' }${dir.modifiers ? `,modifiers:${JSON.stringify(dir.modifiers)}` : '' }},` } } // 只有指令存在執行時任務時,才會返回 res if (hasRuntime) { return res.slice(0, -1) + ']' } }
genDirectives() 方法
檔案位置:src\compiler\codegen\index.js
/* 遍歷屬性陣列 props,得到所有屬性組成的字串 如果不存在動態屬性,則返回:'attrName,attrVal,...' 如果存在動態屬性,則返回:'_d(靜態屬性字串, 動態屬性字串)' */ function genProps(props: Array<ASTAttr>): string { // 靜態屬性 let staticProps = `` // 動態屬性 let dynamicProps = `` // 遍歷屬性陣列 for (let i = 0; i < props.length; i++) { // 屬性 const prop = props[i] // 屬性值 const value = __WEEX__ ? generateValue(prop.value) : transformSpecialNewlines(prop.value) if (prop.dynamic) { // 動態屬性,`dAttrName,dAttrVal,...` dynamicProps += `${prop.name},${value},` } else { // 靜態屬性,'attrName:attrVal,...' staticProps += `"${prop.name}":${value},` } } // 閉合靜態屬性字串,並去掉靜態屬性最後的 ',' staticProps = `{${staticProps.slice(0, -1)}}` if (dynamicProps) { // 如果存在動態屬性則返回:_d(靜態屬性字串,動態屬性字串) return `_d(${staticProps},[${dynamicProps.slice(0, -1)}])` } else { // 說明屬性陣列中不存在動態屬性,直接返回靜態屬性字串 return staticProps } }
genHandlers() 方法
檔案位置:src\compiler\codegen\events.js
/* 生成自定義事件的程式碼 動態:'nativeOn|on_d(staticHandlers, [dynamicHandlers])' 靜態:`nativeOn|on${staticHandlers}` */ export function genHandlers ( events: ASTElementHandlers, isNative: boolean ): string { // 原生為 nativeOn,否則為 on const prefix = isNative ? 'nativeOn:' : 'on:' // 靜態 let staticHandlers = `` // 動態 let dynamicHandlers = `` /* 遍歷 events 陣列 events = [{ name: { value: 回撥函數名, ... } }] */ for (const name in events) { const handlerCode = genHandler(events[name]) if (events[name] && events[name].dynamic) { // 動態,dynamicHandles = `eventName,handleCode,...,` dynamicHandlers += `${name},${handlerCode},` } else { // staticHandlers = `eventName:handleCode,...,` staticHandlers += `"${name}":${handlerCode},` } } // 閉合靜態事件處理程式碼字串,去除末尾的 ',' staticHandlers = `{${staticHandlers.slice(0, -1)}}` if (dynamicHandlers) { // 動態,on_d(statickHandles, [dynamicHandlers]) return prefix + `_d(${staticHandlers},[${dynamicHandlers.slice(0, -1)}])` } else { // 靜態,`on${staticHandlers}` return prefix + staticHandlers } }
genStatic() 方法
檔案位置:src\compiler\codegen\index.js
/* 生成靜態節點的渲染函數 1、將當前靜態節點的渲染函數放到 staticRenderFns 陣列中 2、返回一個可執行函數 _m(idx, true or '') hoist static sub-trees out */ function genStatic(el: ASTElement, state: CodegenState): string { // 標記當前靜態節點已經被處理過了 el.staticProcessed = true /* 某些元素(模板)在 v-pre 節點中需要有不同的行為 所有 pre 節點都是靜態根,因此可將其用作包裝狀態更改並在退出 pre 節點時將其重置 */ const originalPreState = state.pre if (el.pre) { state.pre = el.pre } /* 將靜態根節點的渲染函數 push 到 staticRenderFns 陣列中, 比如:[`with(this){return _c(tag, data, children)}`] */ state.staticRenderFns.push(`with(this){return ${genElement(el, state)}}`) state.pre = originalPreState /* 返回一個可執行函數:_m(idx, true or '') idx = 當前靜態節點的渲染函數在 staticRenderFns 陣列中下標 */ return `_m(${state.staticRenderFns.length - 1 }${el.staticInFor ? ',true' : '' })` }
genOnce() 方法
檔案位置:src\compiler\codegen\index.js
/* 處理帶有 v-once 指令的節點,結果會有三種: 1、當前節點存在 v-if 指令,得到一個三元表示式,condition ? render1 : render2 2、當前節點是一個包含在 v-for 指令內部的靜態節點, 得到 `_o(_c(tag, data, children), number, key)` 3、當前節點就是一個單純的 v-once 節點,得到 `_m(idx, true of '')` v-once */ function genOnce(el: ASTElement, state: CodegenState): string { // 標記當前節點的 v-once 指令已經被處理過了 el.onceProcessed = true if (el.if && !el.ifProcessed) { /* 如果含有 v-if 指令 && if 指令沒有被處理過 則處理帶有 v-if 指令的節點,最終得到一個三元表示式: condition ? render1 : render2 */ return genIf(el, state) } else if (el.staticInFor) { /* 說明當前節點是被包裹在還有 v-for 指令節點內部的靜態節點 獲取 v-for 指令的 key */ let key = '' let parent = el.parent while (parent) { if (parent.for) { key = parent.key break } parent = parent.parent } // key 不存在則給出提示,v-once 節點只能用於帶有 key 的 v-for 節點內部 if (!key) { process.env.NODE_ENV !== 'production' && state.warn( `v-once can only be used inside v-for that is keyed. `, el.rawAttrsMap['v-once'] ) return genElement(el, state) } // 生成 `_o(_c(tag, data, children), number, key)` return `_o(${genElement(el, state)},${state.onceId++},${key})` } else { /* 上面幾種情況都不符合,說明就是一個簡單的靜態節點, 和處理靜態根節點時的操作一樣,得到 _m(idx, true or '') */ return genStatic(el, state) } }
genFor() 方法
檔案位置:src\compiler\codegen\index.js
/* 處理節點上 v-for 指令 得到 `_l(exp, function(alias, iterator1, iterator2){return _c(tag, data, children)})` */ export function genFor( el: any, state: CodegenState, altGen?: Function, altHelper?: string ): string { // v-for 的迭代器,比如 一個陣列 const exp = el.for // 迭代時的別名 const alias = el.alias // iterator 為 v-for = "(item ,idx) in obj" 時會有,比如 iterator1 = idx const iterator1 = el.iterator1 ? `,${el.iterator1}` : '' const iterator2 = el.iterator2 ? `,${el.iterator2}` : '' // 提示,v-for 指令在元件上時必須使用 key if (process.env.NODE_ENV !== 'production' && state.maybeComponent(el) && el.tag !== 'slot' && el.tag !== 'template' && !el.key ) { state.warn( `<${el.tag} v-for="${alias} in ${exp}">: component lists rendered with ` + `v-for should have explicit keys. ` + `See https://vuejs.org/guide/list.html#key for more info.`, el.rawAttrsMap['v-for'], true /* tip */ ) } // 標記當前節點上的 v-for 指令已經被處理過了 el.forProcessed = true // avoid recursion // 返回 `_l(exp, function(alias, iterator1, iterator2){return _c(tag, data, children)})` return `${altHelper || '_l'}((${exp}),` + `function(${alias}${iterator1}${iterator2}){` + `return ${(altGen || genElement)(el, state)}` + '})' }
genIf() 方法
檔案位置:src\compiler\codegen\index.js
// 處理帶有 v-if 指令的節點,最終得到一個三元表示式,condition ? render1 : render2 export function genIf( el: any, state: CodegenState, altGen?: Function, altEmpty?: string ): string { // 標記當前節點的 v-if 指令已經被處理過了,避免無效的遞迴 el.ifProcessed = true // avoid recursion // 得到三元表示式,condition ? render1 : render2 return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty) } function genIfConditions( conditions: ASTIfConditions, state: CodegenState, altGen?: Function, altEmpty?: string ): string { // 長度若為空,則直接返回一個空節點渲染函數 if (!conditions.length) { return altEmpty || '_e()' } // 從 conditions 陣列中拿出第一個條件物件 { exp, block } const condition = conditions.shift() // 返回結果是一個三元表示式字串,condition ? 渲染函數1 : 渲染函數2 if (condition.exp) { /* 如果 condition.exp 條件成立,則得到一個三元表示式, 如果條件不成立,則通過遞迴的方式找 conditions 陣列中下一個元素, 直到找到條件成立的元素,然後返回一個三元表示式 */ return `(${condition.exp})?${genTernaryExp(condition.block) }:${genIfConditions(conditions, state, altGen, altEmpty) }` } else { return `${genTernaryExp(condition.block)}` } // v-if with v-once should generate code like (a)?_m(0):_m(1) function genTernaryExp(el) { return altGen ? altGen(el, state) : el.once ? genOnce(el, state) : genElement(el, state) } }
genSlot() 方法
檔案位置:src\compiler\codegen\index.js
/* 生成插槽的渲染函數,得到:_t(slotName, children, attrs, bind) */ function genSlot(el: ASTElement, state: CodegenState): string { // 插槽名稱 const slotName = el.slotName || '"default"' // 生成所有的子節點 const children = genChildren(el, state) // 結果字串,_t(slotName, children, attrs, bind) let res = `_t(${slotName}${children ? `,function(){return ${children}}` : ''}` const attrs = el.attrs || el.dynamicAttrs ? genProps((el.attrs || []).concat(el.dynamicAttrs || []).map(attr => ({ // slot props are camelized name: camelize(attr.name), value: attr.value, dynamic: attr.dynamic }))) : null const bind = el.attrsMap['v-bind'] if ((attrs || bind) && !children) { res += `,null` } if (attrs) { res += `,${attrs}` } if (bind) { res += `${attrs ? '' : ',null'},${bind}` } return res + ')' }
genComponent() 方法
檔案位置:src\compiler\codegen\index.js
/* 生成動態元件的渲染函數,返回 `_c(compName, data, children)` componentName is el.component, take it as argument to shun flow's pessimistic refinement */ function genComponent( componentName: string, el: ASTElement, state: CodegenState ): string { // 所有的子節點 const children = el.inlineTemplate ? null : genChildren(el, state, true) // 返回 `_c(compName, data, children)`,compName 是 is 屬性的值 return `_c(${componentName},${genData(el, state)}${children ? `,${children}` : '' })` }
渲染函數的生成過程是什麼?
編譯器生成的渲染有兩類:
render
函數,負責生成動態節點的 vnode
staticRenderFns
陣列中的 靜態渲染函數
,負責生成靜態節點的 vnode
渲染函數的生成過程,其實就是在遍歷 AST 節點,通過遞迴的方式處理每個節點,最後生成格式如:_c(tag, attr, children, normalizationType)
:
tag
是標籤名attr
是屬性物件children
是子節點組成的陣列,其中每個元素的格式都是 _c(tag, attr, children, normalizationTYpe)
的形式,normalization
表示節點的規範化型別,是一個數位 0、1、2靜態節點是怎麼處理的?
靜態節點的處理分為兩步:
vnode
函數放到 staticRenderFns
陣列中_m(idx)
的可執行函數,即執行 staticRenderFns
陣列中下標為 idx
的函數,生成靜態節點的 vnode
v-once、v-if、v-for、元件 等都是怎麼處理的?
v-once
節點處理方式 和 靜態節點
一致v-if
節點的處理結果是一個 三元表示式
v-for
節點的處理結果是可執行的 _l
函數,該函數負責生成 v-for
節點的 vnode
_c(compName)
的可執行程式碼,生成元件的 vnode
【相關推薦:】
以上就是解析原始碼,看看 vue 編譯器是怎麼生成渲染函數的!的詳細內容,更多請關注TW511.COM其它相關文章!