preface
This article is the last part of the Vue compiler. The first two parts are: Vue source code interpretation (8) — compiler parsing, and Vue source code interpretation (9) — compiler optimization.
Starting from the HTML template string, all tags and attributes on the tags are parsed to get the AST syntax tree. Then, static tags are made based on the AST syntax tree. First, whether each node is static or static is marked, and then the static root node is marked. These static root node updates can be skipped in subsequent updates to improve performance.
This last section is about generating render functions from the AST.
The target
Get a deeper understanding of how rendering functions are generated, how the compiler turns the AST into runtime code, and what does the htML-like template we wrote end up with?
The source code interpretation
The entrance
/src/compiler/index.js
/** * All this work has been done with the sole purpose of building platform-specific compilation options. For example, web platform * * 1, parse HTML template into AST * 2, static mark ast tree * 3, ast generation rendering function * static rendering function into code. StaticRenderFns array * code Execute the render function to get vNode */ for the dynamic render function *
export const createCompiler = createCompilerCreator(function baseCompile (template: string, options: CompilerOptions) :CompiledResult {
// Parse the template into an AST. Each AST object has all the information of the element, such as label information, attribute information, slot information, parent node, child node, etc.
Start and options.end are two methods that handle the start and end tags
const ast = parse(template.trim(), options)
// Optimize by traversing the AST, marking each node statically
// Mark whether each node is static or not, and then further mark the static root node
// This allows you to skip the static nodes in subsequent updates
// Mark the static root, which is used in the render function generation stage, to generate the render function of the static root node
if(options.optimize ! = =false) {
optimize(ast, options)
}
// Code generation that converts the AST to the string form of the executable render function
// code = {
// render: `with(this){return ${_c(tag, data, children, normalizationType)}}`,
// staticRenderFns: [_c(tag, data, children, normalizationType), ...]
// }
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
Copy the code
generate
/src/compiler/codegen/index.js
/** * generate render function * from AST@returns { * render: `with(this){return _c(tag, data, children)}`,
* staticRenderFns: state.staticRenderFns
* }
*/
export function generate(
ast: ASTElement | void,
options: CompilerOptions
) :CodegenResult {
// Instantiate the CodegenState object, some of which will be needed to generate code
const state = new CodegenState(options)
// Generate code for string format, such as: '_c(tag, data, children, normalizationType)'
// data forms a JSON string for the attributes on the node, such as '{key: xx, ref: xx,... } '
// children is a string array composed of string format code for all child nodes, format:
// `['_c(tag, data, children)', ...] , normalizationType `,
// The final normalization is the fourth parameter of _c,
// Indicates the normalized type of the node
// Code does not have to be _c, but it can be something else. For example, if the entire component is static, the result will be _m(0).
const code = ast ? genElement(ast, state) : '_c("div")'
return {
render: `with(this){return ${code}} `.staticRenderFns: state.staticRenderFns
}
}
Copy the code
genElement
/src/compiler/codegen/index.js
Reading Suggestions:
Read the statement portion of the final Else module that generates code, the else branch that handles custom components and native tags, to understand what the resulting data format will look like. Then go back to reading genChildren and genData, reading genChildren first, with less code, fully understanding the resulting data structure, and then reading the other branches from top to bottom.
When reading the following code, please put aside the AST object obtained by the Vue source interpretation (8) — the compiler’s parsing (2), because the process of generating the rendering function is the process of processing many properties on the object.
export function genElement(el: ASTElement, state: CodegenState) :string {
if (el.parent) {
el.pre = el.pre || el.parent.pre
}
if(el.staticRoot && ! el.staticProcessed) {/** * process the static root node and generate the rendering function for the node * 1, put the rendering function for the current static node into the staticRenderFns array * 2, return an executable function _m(idx, true or "") */
return genStatic(el, state)
} else if(el.once && ! el.onceProcessed) {/** * process a node with a v-if instruction, and there are three results: * 1, the current node has a V-if instruction, and a ternary expression, condition? render1 : Render2 * 2, the current node is a static node contained inside the V-for directive, resulting in _o(_C (tag, data, children), number, key) * 3, the current node is a pure V-once node. _m(idx, true of "") */
return genOnce(el, state)
} else if(el.for && ! el.forProcessed) {/** * Process the v-for directive on the node * to get '_l(exp, function(alias, iterator1, iterator2){return _c(tag, data, children)})' */
return genFor(el, state)
} else if(el.if && ! el.ifProcessed) {/** * process the node with the v-if directive, resulting in a ternary expression: condition? render1 : render2 */
return genIf(el, state)
} else if (el.tag === 'template'&&! el.slotTarget && ! state.pre) {/** * generate a rendering function for all child nodes and return an array in the following format: * [_c(tag, data, children, normalizationType), ...] * /
return genChildren(el, state) || 'void 0'
} else if (el.tag === 'slot') {
/** * generate the render function for the slot and get * _t(slotName, children, attrs, bind) */
return genSlot(el, state)
} else {
// component or element
// Handle dynamic components and normal elements (custom components, native tags)
let code
if (el.component) {
/** * processing dynamic component, generate dynamic component rendering function * get '_c(compName, data, children)' */
code = genComponent(el.component, el, state)
} else {
// Custom components and native tags go here
let data
if(! el.plain || (el.pre && state.maybeComponent(el))) {// Non-ordinary elements or components with v-pre directives go here, process all the attributes of the node, and return a JSON string,
'{key: xx, ref: xx,... } '
data = genData(el, state)
}
// Process the child nodes to get an array of all the child nodes string format code, format:
// `['_c(tag, data, children)', ...] , normalizationType `,
// The final normalization means the normalized type of the node and is not important as it needs no attention
const children = el.inlineTemplate ? null : genChildren(el, state, true)
// Get the final string format code, format:
// '_c(tag, data, children, normalizationType)'
code = `_c('${el.tag}'${data ? `,${data}` : ' ' // data
}${children ? `,${children}` : ' ' // children
}) `
}
// If you provide a method transformCode,
// Then the final code will be processed by the method of each module.
// The framework does not provide this method, but even if it does, the final format is _c(tag, data, children).
// module transforms
for (let i = 0; i < state.transforms.length; i++) {
code = state.transforms[i](el, code)
}
return code
}
}
Copy the code
genChildren
/src/compiler/codegen/index.js
/** * generate a rendering function for all child nodes and return an array in the format: * [_c(tag, data, children, normalizationType),...] * /
export function genChildren(el: ASTElement, state: CodegenState, checkSkip? : boolean, altGenElement? :Function, altGenNode? :Function
) :string | void {
// All child nodes
const children = el.children
if (children.length) {
// The first child
const el: any = children[0]
// optimize single v-for
if (children.length === 1&& el.for && el.tag ! = ='template'&& el.tag ! = ='slot'
) {
// Optimize so that only one child has a V-for directive on it and the child tag is not template or slot
// The optimized approach is to call genElement directly to generate the render function for the node, without having to go through the following loop and then call genCode to get the render function
const normalizationType = checkSkip
? state.maybeComponent(el) ? ` `, 1 : ` `, 0
: ` `
return `${(altGenElement || genElement)(el, state)}${normalizationType}`
}
// Get the normalized type of the node and return a number 0, 1, 2
const normalizationType = checkSkip
? getNormalizationType(children, state.maybeComponent)
: 0
// function, a function that generates code
const gen = altGenNode || genNode
// Return an array, each element of which is a rendering function for a child node,
['_c(tag, data, children, normalizationType)',...]
return ` [${children.map(c => gen(c, state)).join(', ')}]${normalizationType ? `,${normalizationType}` : ' '
}`}}Copy the code
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)
}
}
Copy the code
genText
/src/compiler/codegen/index.js
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))
}) `
}
Copy the code
genComment
/src/compiler/codegen/index.js
export function genComment(comment: ASTText) :string {
return `_e(The ${JSON.stringify(comment.text)}) `
}
Copy the code
genData
/src/compiler/codegen/index.js
Data = {key: xx, ref: xx,... } * /
export function genData(el: ASTElement, state: CodegenState) :string {
// A JSON string composed of the attributes of the node
let data = '{'
// Process the instructions first, because the instructions may change these attributes before generating other attributes
// Execute instructions to compile methods such as Web platform V-text, V-HTML, v-Model, and then add corresponding attributes to the EL object.
// For example, v-text: el.textContent = _s(value, dir)
// v-html: el.innerhtml = _s(value, dir)
// When the directive is running and there is a task, such as the V-Model, returns directives: [{name, rawName, value, arg, modifiers},...}]
// directives first.
// directives may mutate the el's other properties before they are generated.
const dirs = genDirectives(el, state)
if (dirs) data += dirs + ', '
Key, data = {key: xx}
if (el.key) {
data += `key:${el.key}, `
}
// ref, data = {ref: xx}
if (el.ref) {
data += `ref:${el.ref}, `
}
// The node with the ref attribute is inside the node with the V-for directive, data = {refInFor: true}
if (el.refInFor) {
data += `refInFor:true,`
}
// pre, v-pre directive, data = {pre: true}
if (el.pre) {
data += `pre:true,`
}
// Dynamic component, data = {tag: 'component'}
// record original tag name for components using "is" attribute
if (el.component) {
data += `tag:"${el.tag}", `
}
// Execute the genData method of the module (class, style) for the node,
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)
}
// Other attributes, get data = {attrs: static attribute string} or
// data = {attrs: '_d(static attribute string, dynamic attribute string)'}
// attributes
if (el.attrs) {
data += `attrs:${genProps(el.attrs)}, `
}
// DOM props, same as el.attrs
if (el.props) {
data += `domProps:${genProps(el.props)}, `
}
${eventName}:handleCode '} or {' on_d(${eventName}:handleCode ', '${eventName},handleCode')}
// event handlers
if (el.events) {
data += `${genHandlers(el.events, false)}, `
}
// Events with.native modifier,
// 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 = {scoped slots: '_u(XXX)'}
if (el.scopedSlots) {
data += `${genScopedSlots(el, el.scopedSlots, state)}, `
}
// Process the v-Model attribute to get
// 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, handles the inline template, and gets
// data = {inlineTemplate: {render: function() {render: function}, staticRenderFns: [function() {},...] }}
if (el.inlineTemplate) {
const inlineTemplate = genInlineTemplate(el, state)
if (inlineTemplate) {
data += `${inlineTemplate}, `}}// Delete the comma at the end of the JSON string and add the closing parentheses}
data = data.replace($/ /,.' ') + '} '
// v-bind dynamic argument wrap
// v-bind with dynamic arguments must be applied using the same v-bind object
// merge helper so that class/style/mustUseProp attrs are handled correctly.
if (el.dynamicAttrs) {
// Dynamic attributes exist, data = '_b(data, tag, static attribute string or _d(static attribute string, dynamic attribute string))'
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
}
Copy the code
genDirectives
/src/compiler/codegen/index.js
Reading advice: This section can also be put behind other methods if you want to delve deeper into how the V-Model is implemented
/** * Runs the compilation method of directives and returns directives if there is a runtime task: [{name, rawName, value, arg, modifiers},...}] */
function genDirectives(el: ASTElement, state: CodegenState) :string | void {
// Get the instruction array
const dirs = el.directives
// If there is no command, it ends directly
if(! dirs)return
// The result of processing the instruction
let res = 'directives:['
// Tag, which indicates whether the directive needs to complete tasks at runtime, such as input events for the V-Model
let hasRuntime = false
let i, l, dir, needRuntime
// Iterate over the instruction array
for (i = 0, l = dirs.length; i < l; i++) {
dir = dirs[i]
needRuntime = true
// Get the processing method of the node's current instruction, such as the Web platform V-HTML, V-Text, V-Model
const gen: DirectiveFunction = state.directives[dir.name]
if (gen) {
// Execute the compilation method of the instruction, returning true if the instruction still needs to perform some part of the runtime task, such as v-model
// compile-time directive that manipulates AST.
// returns true if it also needs a runtime counterpart.needRuntime = !! gen(el, dir, state.warn) }if (needRuntime) {
// indicates that the directive has a task at runtime
hasRuntime = true
// res = directives:[{ name, rawName, value, arg, modifiers }, ...]
res += `{name:"${dir.name}",rawName:"${dir.rawName}"${dir.value ? `,value:(${dir.value}),expression:The ${JSON.stringify(dir.value)}` : ' '
}${dir.arg ? `,arg:${dir.isDynamicArg ? dir.arg : `"${dir.arg}"`}` : ' '
}${dir.modifiers ? `,modifiers:The ${JSON.stringify(dir.modifiers)}` : ' '
}}, `}}if (hasRuntime) {
// That is, res is returned only if the instruction exists in a runtime task
return res.slice(0, -1) + '] '}}Copy the code
genProps
/src/compiler/codegen/index.js
/** * Iterates through the attribute array props to get a string of all the attributes * if no dynamic attribute exists, return: * 'attrName,attrVal,... '* if dynamic attributes exist, return: * '_d(static attribute string, dynamic attribute string)' */
function genProps(props: Array<ASTAttr>) :string {
// Static properties
let staticProps = ` `
// Dynamic properties
let dynamicProps = ` `
// Iterate over the attribute array
for (let i = 0; i < props.length; i++) {
/ / property
const prop = props[i]
/ / property values
const value = __WEEX__
? generateValue(prop.value)
: transformSpecialNewlines(prop.value)
if (prop.dynamic) {
// Dynamic properties, 'dAttrName,dAttrVal,... `
dynamicProps += `${prop.name}.${value}, `
} else {
// Static attribute, 'attrName,attrVal,... '
staticProps += `"${prop.name}":${value}, `}}// Remove the comma at the end of the static attribute
staticProps = ` {${staticProps.slice(0, -1)}} `
if (dynamicProps) {
// If dynamic properties exist, return:
// _d(static attribute string, dynamic attribute string)
return `_d(${staticProps}[${dynamicProps.slice(0, -1)}]) `
} else {
// Return a string of static attributes
return staticProps
}
}
Copy the code
genHandlers
/src/compiler/codegen/events.js
/ * * * to generate a custom event dynamic code * : 'nativeOn | on_d (staticHandlers, [dynamicHandlers])' * static: ` nativeOn | on ${staticHandlers} ` * /
export function genHandlers (events: ASTElementHandlers, isNative: boolean) :string {
// Native: nativeOn, otherwise on
const prefix = isNative ? 'nativeOn:' : 'on:'
/ / static
let staticHandlers = ` `
/ / dynamic
let dynamicHandlers = ` `
// Iterate through the Events array
// events = [{name: {value: callback,...}}]
for (const name in events) {
// Get the name of the callback function for the specified event, i.e. This. methodName or [this.methodName1,...
const handlerCode = genHandler(events[name])
if (events[name] && events[name].dynamic) {
// Handles = 'eventName,handleCode,... , `
dynamicHandlers += `${name}.${handlerCode}, `
} else {
StaticHandles = '"eventName":handleCode,'
staticHandlers += `"${name}":${handlerCode}, `}}// Remove the trailing comma
staticHandlers = ` {${staticHandlers.slice(0, -1)}} `
if (dynamicHandlers) {
// dynamic, on_d(statickHandles, [dynamicHandlers])
return prefix + `_d(${staticHandlers}[${dynamicHandlers.slice(0, -1)}]) `
} else {
// Static, 'on${staticHandlers}'
return prefix + staticHandlers
}
}
Copy the code
genStatic
/src/compiler/codegen/index.js
/** * generate static node render function * 1, put the current static node render function in the staticRenderFns array * 2, return an executable function _m(idx, true or "") */
// hoist static sub-trees out
function genStatic(el: ASTElement, state: CodegenState) :string {
// Indicates that the current static node has been processed
el.staticProcessed = true
// Some elements (templates) need to behave differently inside of a v-pre
// node. All pre nodes are static roots, so we can use this as a location to
// wrap a state change and reset it upon exiting the pre node.
const originalPreState = state.pre
if (el.pre) {
state.pre = el.pre
}
// Push the rendering function of the static root node into the staticRenderFns array, such as:
// [`with(this){return _c(tag, data, children)}`]
state.staticRenderFns.push(`with(this){return ${genElement(el, state)}} `)
state.pre = originalPreState
Return an executable function: _m(idx, true or "")
// idx = The rendering function of the current static node is subscripted in the staticRenderFns array
return `_m(${state.staticRenderFns.length - 1
}${el.staticInFor ? ',true' : ' '
}) `
}
Copy the code
genOnce
/src/compiler/codegen/index.js
/** * process a node with a v-if instruction, and there are three results: * 1, the current node has a V-if instruction, and a ternary expression, condition? render1 : Render2 * 2, the current node is a static node contained inside the V-for directive, resulting in _o(_C (tag, data, children), number, key) * 3, the current node is a pure V-once node. _m(idx, true of "") */
function genOnce(el: ASTElement, state: CodegenState) :string {
// Indicates that the current node's V-once directive has been processed
el.onceProcessed = true
if(el.if && ! el.ifProcessed) {// If there are v-if directives && if directives that have not been processed, go here
// Process the node with the v-if instruction, resulting in a ternary expression, condition? render1 : render2
return genIf(el, state)
} else if (el.staticInFor) {
// Note that the current node is a static node wrapped inside a node with the V-for directive
// Get the key for the V-for instruction
let key = ' '
let parent = el.parent
while (parent) {
if (parent.for) {
key = parent.key
break
}
parent = parent.parent
}
The v-once node can only be used inside a V-for node with a key
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)
}
// Generate '_o(_c(tag, data, children), number, key)'
return `_o(${genElement(el, state)}.${state.onceId++}.${key}) `
} else {
// If none of the above is true, it is a simple static node, just like the static root node.
// get _m(idx, true or "")
return genStatic(el, state)
}
}
Copy the code
genFor
/src/compiler/codegen/index.js
/** * Process the v-for directive on the node * to get '_l(exp, function(alias, iterator1, iterator2){return _c(tag, data, children)})' */
export function genFor(el: any, state: CodegenState, altGen? :Function, altHelper? : string) :string {
// A v-for iterator, such as an array
const exp = el.for
// The alias for iteration
const alias = el.alias
// if iterator is v-for = "(item,idx) in obj", for example, iterator1 = idx
const iterator1 = el.iterator1 ? `,${el.iterator1}` : ' '
const iterator2 = el.iterator2 ? `,${el.iterator2}` : ' '
// Note that the v-for directive must use the key on the component
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 */)}// Indicates that the v-for directive on the current node has been processed
el.forProcessed = true // avoid r
Function (exp, function(alias, iterator1, iterator2){return _c(tag, data, children)})
return `${altHelper || '_l'}((${exp}), ` +
`function(${alias}${iterator1}${iterator2}) {` +
`return ${(altGen || genElement)(el, state)}` +
'}) '
}
Copy the code
genIf
/src/compiler/codegen/index.js
/** * process the node with the v-if directive, resulting in a ternary expression, condition? render1 : render2 */
export function genIf(el: any, state: CodegenState, altGen? :Function, altEmpty? : string) :string {
// Indicates that the current node's v-if directive has been processed to avoid invalid recursion
el.ifProcessed = true // avoid recursion
// Obtain the ternary expression, condition? render1 : render2
return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty)
}
function genIfConditions(conditions: ASTIfConditions, state: CodegenState, altGen? :Function, altEmpty? : string) :string {
// If the length is null, return an empty node rendering function
if(! conditions.length) {return altEmpty || '_e()'
}
// Take the first condition object from the conditions array {exp, block}
const condition = conditions.shift()
// Return a ternary expression string, condition? Render function 1: Render function 2
if (condition.exp) {
// If the condition.exp condition is true, then we get a teradata expression,
// If the conditions are not valid, we recursively search for the next element in the conditions array.
// Return a ternary expression until the condition is found
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)
}
}
Copy the code
genSlot
/src/compiler/codegen/index.js
/** * generate the render function for the slot and get * _t(slotName, children, attrs, bind) */
function genSlot(el: ASTElement, state: CodegenState) :string {
// Slot name
const slotName = el.slotName || '"default"'
// Generate all child nodes
const children = genChildren(el, state)
// Result string, _t(slotName, children, attrs, bind)
let res = `_t(${slotName}${children ? `,${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 + ') '
}
Copy the code
genComponent
/src/compiler/codegen/index.js
// componentName is el.component, take it as argument to shun flow's pessimistic refinement
/** * The rendering function that generates dynamic components * returns' _c(compName, data, children) '*/
function genComponent(componentName: string, el: ASTElement, state: CodegenState) :string {
// All child nodes
const children = el.inlineTemplate ? null : genChildren(el, state, true)
// Return '_c(compName, data, children)'
// compName is the value of the IS attribute
return `_c(${componentName}.${genData(el, state)}${children ? `,${children}` : ' '
}) `
}
Copy the code
conclusion
-
Interviewer: What does Vue’s compiler do briefly?
A:
Vue’s compiler does three things:
-
Parse the component’s HTML template into an AST object
-
Optimization, traversing the AST, making static tags for each node, marking whether it is static node or not, and then further marking the static root node, so that these static nodes can be skipped in the process of subsequent updates; The tag static root is used in the render function generation phase to generate the render function for the static root node
-
The staticRenderFns array contains all static node render functions
-
-
Interviewer: Tell me more about the rendering function generation process
A:
There are two types of render generated by the compiler:
-
The first is a render function that generates a vNode with dynamic nodes
-
The second is static rendering functions placed in an array called staticRenderFns, which are responsible for generating vNodes with static nodes
The rendering function generation process is actually traversing the AST nodes, processing each node through a recursive way, and finally generating the result of the form: _c(Tag, attr, children, normalizationType). Tag is the tag name, attr is the attribute object, children is an array of children, each of which is of the form _c(Tag, attr, children, normalizationTYpe). Normalization means the normalized type of the node, which is a number 0, 1, 2, and is not important.
In the process of dealing with AST nodes, we need to focus on the common questions in the interview:
-
What about static nodes
The processing of static nodes is divided into two steps:
-
Put the generating static node vnode function in the staticRenderFns array
-
Return an executable function _m(idx) that executes the function of staticRenderFns with subscript IDx, generating a vnode with static nodes
-
-
How to deal with v-ONCE, V-IF, V-for, components, etc
-
The processing mode of pure V-Once nodes is the same as that of static nodes
-
The result of processing the V-if node is a ternary expression
-
The result of the processing of the V-for node is the executable _L function, which is responsible for generating the VNode of the V-for node
-
The result of component processing is the same as that of ordinary elements. The result is executable code of the form _c(compName), which generates vNode of the component
-
-
At this point, the Vue compiler’s interpretation of the source code ends. I believe you will inevitably have a feeling in the process of reading. That’s okay. The compiler is the most complicated part of the framework, the most difficult part to understand and the most code. Be sure to calm down to read a few more times, encounter can not understand the place, be sure to work hard, through example code and breakpoint debugging way to help yourself understand.
After you’ve read it a few times, you’ll probably get better, but you’ll still get a little dizzy in some places, and that’s fine, that’s normal. After all, this is a framework compiler that handles a lot of stuff, and you just need to understand the core ideas (template parsing, static markup, code generation). A hand-written Vue series will follow, and a simplified implementation will be available in the compiler section to help deepen the understanding.
When the compiler reads through it, it will find that the code generated by the compiler is wrapped with, such as:
<div id="app">
<div v-for="item in arr" :key="item">{{ item }}</div>
</div>
Copy the code
After compilation, generate:
with (this) {
return _c(
'div',
{
attrs:
{
"id": "app"
}
},
_l(
(arr),
function (item) {
return _c(
'div',
{
key: item
},
[_v(_s(item))]
)
}
),
0)}Copy the code
As we all know, the with statement extends the scope chain, so the _c, _L, _v, and _s in the generated code are methods on this, meaning that when executed at run time, these methods will generate vnodes for each node.
Therefore, in connection with the previous knowledge, the entire implementation process of responsive data update is:
-
Responsively intercepts updates to the data
-
The DEP informs Watcher to make an asynchronous update
-
The component update function updateComponent is executed when Watcher updates
-
The vnode of the vm._render generated component is executed first, which executes the compiler generated function
-
Question:
-
What are the _c, _l, _v, _s methods in the render function?
-
How do they generate vNodes?
-
The next article Vue source code Interpretation (11) — Render Helper will bring a detailed interpretation of this knowledge, which is often asked in interviews: for example: what is the principle of V-for?
Form a complete set of video
Vue source code interpretation (10) — compiler generation rendering function
Please focus on
Welcome everyone to follow my gold mining account and B station, if the content has to help you, welcome everyone to like, collect + attention
link
-
Vue source code interpretation (1) – preface
-
Vue source code interpretation (2) — Vue initialization process
-
Vue source code interpretation (3) – response principle
-
Vue source code interpretation (4) — asynchronous update
-
Vue source code Interpretation (5) — Global API
-
Vue source code interpretation (6) — instance method
-
(7) — Hook Event
-
Vue source code interpretation (8) — compiler parsing
-
Vue source code interpretation (9) — compiler optimization
-
Vue source code interpretation (10) — compiler generation rendering function
-
(11) — Render helper
-
Vue source code interpretation (12) — patch
Learning exchange group
link