この記事では、ソース コードを深く分析し、vue ソース コードから問題を確認し、vue コンパイラーがレンダリング関数をどのように生成するかを確認します。
最初の 2 つの記事では、主に 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#<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">// 得到返回值,格式为:`_e(xxxx)`
export function genComment (comment: ASTText): string {
return `_e(${JSON.stringify(comment.text)})`
}</pre><div class="contentsignin">ログイン後にコピー</div></div>
ファイルの場所:
src\compiler\codegen\index.js<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">// 得到返回值,格式为:`_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))
})`
}</pre><div class="contentsignin">ログイン後にコピー</div></div>
# ファイルの場所: 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}` : '' })` }
概要レンダリング関数の生成プロセスとは何ですか?
コンパイラによって生成されるレンダリングには 2 つのタイプがあります。関数。render
vnode
staticRenderFns
静的レンダリング関数
#vnode
の生成プロセスレンダリング関数は実際に AST ノードを走査し、各ノードを再帰的に処理し、最終的に :_c(tag, attr, Children,normalizationType)
: のような形式を生成します。<ul>
<li>
<code>tag
はタグ名です attr
は属性オブジェクト children
で構成されます子ノードの配列。各要素の形式は _c(tag, attr, Children,normalizationTYpe)
の形式です。 はノードの正規化タイプを表しますこれは、数値 0、1、2です。
静的ノードの処理は分割されています。静的ノードはどのように処理されますか?
関数を
staticRenderFns配列に配置します
実行可能関数は、
staticRenderFns 配列内の添字
idx を使用して関数を実行し、
vnode
簡単#v-once、v-if、v-for、コンポーネントなどを扱う方法
static node
三項式##です
#v-for 関数であり、この関数は
vnode## の生成を担当します。
v-for ノード
#コンポーネントの処理結果は通常の要素と同じで、得られるのは _c(compName)## の形式の実行コードです#、コンポーネントの
vnode# が生成されます。 ##[関連する推奨事項:
vue.js チュートリアル ]
以上がソース コードを解析して、vue コンパイラーがレンダリング関数をどのように生成するかを確認してください。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。