preface
As mentioned in the previous article, when a component is updated, it is necessary to execute the rendering function generated by the compiler to obtain the vNode of the component, and the rendering function generates the Vnode through the _c, _L, _V, _S methods. Such as:
- Ordinary nodes are compiled into executable _c functions
- The V-for node is compiled into an executable _L function
- .
What is render Helper?
Deep source
Entry position
{render: return by generate() method ‘with(this){return ${code}}’} with(this){return ${code}}’} _c, _l, _v, _s;
SRC \core\instance\render. Js: SRC \core\instance\render. Js: SRC \core\instance\render.
export function generate(
ast: ASTElement | void./ / ast objects
options: CompilerOptions // Compile options
) :CodegenResult {
/* Instantiate the CodegenState object, taking the compile option to get state, which has most of the same properties as options */
const state = new CodegenState(options)
/* Generates string format code such as: '_c(tag, data, children, normalizationType)' - data forms JSON strings for attributes on nodes such as '{key: xx, ref: xx,... }' -children is an array of string codes for all child nodes in the format of '['_c(tag, data, children)',... NormalizationType 'and -normalization is the fourth parameter of _C, which means the normalization type of the node. Note: Code does not have to be _c, but it could be something else, such as _m(0) */ if the entire component is static
const code = ast ? (ast.tag === 'script' ? 'null' : genElement(ast, state)) : '_c("div")'
return {
render: `with(this){return ${code}} `.staticRenderFns: state.staticRenderFns
}
}
Copy the code
InstallRenderHelpers () method
SRC \core\instance\render. Js installRenderMixin () helpers ()
export function renderMixin (Vue: Class<Component>) {
// install runtime convenience helpers
// Mount some run-time utility methods on the component instance
installRenderHelpers(Vue.prototype)
// ...
}
Copy the code
File Location:src\core\instance\render-helpers\index.js
/** * mount shorthand renderer functions on instances, which are run time code * these utility functions are used in compiler-generated renderers *@param Target Vue instance */
export function installRenderHelpers (target: any) {
/** All vnodes that contain the v-once directive are treated as static nodes: node.isStatic = true node.key = key node.isOnce = true */
target._o = markOnce
// Convert values to numbers using parseFloat()
target._n = toNumber
If it is an array or a normal object, call json.stringify () to serialize the reference type; otherwise use String to convert to String */
target._s = toString
/* A helper function that renders the V-for list at runtime, iterating over the val value, generating vNodes for each item in turn by executing the render method, and finally returning a VNode array */
target._l = renderList
target._t = renderSlot
/* Determine whether two values are equal: array, plain object, Date object, primitive type (all converted to string comparison) */
target._q = looseEqual
/** corresponds to the indexOf() method of an array, essentially calling looseEqual() */
target._i = looseIndexOf
/* Run the VNode helper to generate the static tree: Execute the rendering function staticRenderFns array, generate the VNode of the static tree and cache it, read it directly from the cache when rendering next time (isInFor must be true)
target._m = renderStatic
target._f = resolveFilter
target._k = checkKeyCodes
target._b = bindObjectProps
/* Create vnodes for text nodes, where: vnode. text = 'XXX' vnode. isComment = false */
target._v = createTextVNode
/* Create vnodes for empty nodes, where: vnode. text = 'XXX' vnode. isComment = true */
target._e = createEmptyVNode
target._u = resolveScopedSlots
target._g = bindObjectListeners
target._d = bindDynamicKeys
target._p = prependModifier
}
Copy the code
target._o = markOnce
File Location:src\core\instance\render-helpers\index.js
MarkOnce () method
/** * Runtime helper for v-once. * Effectively it means marking the node as static with a unique key Mark nodes as static nodes */
export function markOnce (
tree: VNode | Array<VNode>,
index: number,
key: string
) {
/*
Ensure that key= '__once__${index}_mydiv' */
markStatic(tree, `__once__${index}${key ? ` _${key}` : ` `}`.true)
return tree
}
Copy the code
MarkStatic () method
function markStatic (
tree: VNode | Array<VNode>,
key: string,
isOnce: boolean
) {
// tree is an array
if (Array.isArray(tree)) {
// Traverses the tree and marks each tree[I] with markStaticNode()
for (let i = 0; i < tree.length; i++) {
if (tree[i] && typeoftree[i] ! = ='string') {
markStaticNode(tree[i], `${key}_${i}`, isOnce)
}
}
} else {
// Tree is not an array. Call markStaticNode() directly to mark it
markStaticNode(tree, key, isOnce)
}
}
Copy the code
MarkStaticNode () method
/* Add three attributes to VNode: {isStatick: true, key: xx, isOnce: true or false} */
function markStaticNode (node, key, isOnce) {
node.isStatic = true
node.key = key
node.isOnce = isOnce
}
Copy the code
target._l = renderList
File Location:src\core\instance\render-helpers\render-list.js
/** * Runtime helper for rendering V-for lists. ** Runtime helper for rendering V-for lists * Returns a VNode array */ by looping over the render method for arrays, strings, numbers, and objects
export function renderList (val: any, render: ( val: any, keyOrIndex: string | number, index? : number ) => VNode): ?Array<VNode> {
let ret: ?Array<VNode>, i, l, keys, key
*/
if (Array.isArray(val) || typeof val === 'string') {
ret = new Array(val.length)
// Call the render method for each element or character in a loop to get the VNode node
for (i = 0, l = val.length; i < l; i++) {
ret[i] = render(val[i], i)
}
} else if (typeof val === 'number') {
*/
ret = new Array(val)
// Loop through the render method for each number to get the VNode
for (i = 0; i < val; i++) {
ret[i] = render(i + 1, i)
}
} else if (isObject(val)) {
Val is the object
// Val is an iterable
if (hasSymbol && val[Symbol.iterator]) {
ret = []
/* Get the iterable instance, use the while loop as the current iterator, call render to get the VNode */
const iterator: Iterator<any> = val[Symbol.iterator]()
let result = iterator.next()
while(! result.done) { ret.push(render(result.value, ret.length)) result = iterator.next() } }else {
/* Val is a non-iterable with object.keys () for the value of each key on the Object
keys = Object.keys(val)
ret = new Array(keys.length)
for (i = 0, l = keys.length; i < l; i++) {
key = keys[i]
ret[i] = render(val[key], key, i)
}
}
}
/* isDef(ret) --> ret ! == undefined && ret ! = = null! isDef(ret) --> ret === undefined || ret === null */
if(! isDef(ret)) { ret = [] }// Return the VNode array
(ret: any)._isVList = true
return ret
}
Copy the code
target._m = renderStatic
File Location:src\core\instance\render-helpers\render-list.js
/** * Runtime helper for rendering static trees. ** * Generate the VNode of the static tree and cache it for next rendering (isInFor must be true) * 2. Mark the VNode of the static tree statically@param { number} Index represents the subscript index * of the render function of the current static node in the staticRenderFns array@param { boolean} IsInFor indicates whether the current static node is wrapped inside the node containing the V-for instruction */
export function renderStatic (index: number, isInFor: boolean) :VNode | Array<VNode> {
// Cache, the static node is directly fetched from the cache the second time it is rendered
const cached = this._staticTrees || (this._staticTrees = [])
let tree = cached[index]
/* If a static tree has been rendered and is not in the V-for, the same tree can be reused. * /
if(tree && ! isInFor) {return tree
}
/* If the static tree has already been rendered and is in v-for, then we need to re-render the tree through the staticRenderFns array, fetch and execute the corresponding static rendering function, get the new VNode node, and cache the latest result */
tree = cached[index] = this.$options.staticRenderFns[index].call(
this._renderProxy,
null.this // for render fns generated for functional component templates
)
/ * through markStatic method, marking the static node, receiving three parameters: the tree: VNode | Array < VNode > key: string isOnce: Boolean * /
markStatic(tree, `__static__${index}`.false)
return tree
}
Copy the code
vm._c = createElement()
File Location:/src/core/instance/render.js
export function initRender (vm: Component) {.../* Bind the createElement method to this instance to get the current order of render context parameters: Tag, data, children, normalizationType, alwaysNormalize Internal versions are used by rendering functions compiled from templates */
vm._c = (a, b, c, d) = > createElement(vm, a, b, c, d, false)... }Copy the code
The createElement method () method
File Location:src\core\vdom\create-element.js
Function for providing a more flexible interface without getting yelled at by flow */
export function createElement (context: Component, tag: any, data: any, children: any, normalizationType: any, alwaysNormalize: boolean) :VNode | Array<VNode> {
if (Array.isArray(data) || isPrimitive(data)) {
normalizationType = children
children = data
data = undefined
}
if (isTrue(alwaysNormalize)) {
normalizationType = ALWAYS_NORMALIZE
}
// Execute the _createElement method to create a VNode for the component
return _createElement(context, tag, data, children, normalizationType)
}
Copy the code
_createElement () method
File Location:src\core\vdom\create-element.js
/** * Generate vnode, CreateComponent generates Vnode * 2.1 The functional component executes its own render function to generate Vnode * 2.2 A normal component instantiates a VNode and sets four methods on its data.hook object. * These methods are called during the patch phase of the component, and the child component is instantiated, mounted, and rendered *@param {*} Context *@param {*} The tag label *@param {*} Data property JSON String *@param {*} Children array of child nodes *@param {*} NormalizationType Specifies the normalized type * of a node@returns VNode or Array<VNode>
*/
export function _createElement (context: Component, tag? : string | Class<Component> |Function | Object, data? : VNodeData, children? : any, normalizationType? : number) :VNode | Array<VNode> {
if (isDef(data) && isDef((data: any).__ob__)) {
// Attribute cannot be a reactive objectprocess.env.NODE_ENV ! = ='production' && warn(
`Avoid using observed data object as vnode data: The ${JSON.stringify(data)}\n` +
'Always create fresh vnode data objects in each render! ',
context
)
// If the property is a responsive object, an empty VNode is returned
return createEmptyVNode()
}
// object syntax in v-bind
if (isDef(data) && isDef(data.is)) {
tag = data.is
}
if(! tag) {/* When the is attribute of a dynamic component is false and the tag is false, the VNode of an empty node is returned as follows:
in case of component :is set to falsy value */
return createEmptyVNode()
}
Warn against non-primitive key */ warn against primitive key */
if(process.env.NODE_ENV ! = ='production'&& isDef(data) && isDef(data.key) && ! isPrimitive(data.key) ) {if(! __WEEX__ || ! ('@binding' in data.key)) {
warn(
'Avoid using non-primitive value as key, ' +
'use string/number value instead.',
context
)
}
}
/* If there is only one function in the child node array, use it as the default slot and empty the child node list
if (Array.isArray(children) &&
typeof children[0= = ='function'
) {
data = data || {}
data.scopedSlots = { default: children[0] }
children.length = 0
}
// Normalize the child elements
if (normalizationType === ALWAYS_NORMALIZE) {
children = normalizeChildren(children)
} else if (normalizationType === SIMPLE_NORMALIZE) {
children = simpleNormalizeChildren(children)
}
let vnode, ns
if (typeof tag === 'string') {
If the /* tag is a string, the tag has three possibilities: platform reserved tag, custom component, and unknown tag */
let Ctor
// Namespace
ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
if (config.isReservedTag(tag)) {
/* Platform built-in elements */
if(process.env.NODE_ENV ! = ='production'&& isDef(data) && isDef(data.nativeOn) && data.tag ! = ='component') {
/* The.native of the V-ON directive only works on components, such as: < comp@click ="outClick"> --> Clicking on a component does not trigger the outClick method Methods * /
warn(
`The .native modifier for v-on is only valid on components but it was used on <${tag}>. `,
context
)
}
// Instantiate a VNode
vnode = new VNode(
config.parsePlatformTagName(tag), data, children,
undefined.undefined, context
)
} else if((! data || ! data.pre) && isDef(Ctor = resolveAsset(context.$options,'components', tag))) {
/* Tag is a custom component - find the component constructor with the given tag name in this. Codes.com ponents object to create a VNode for the component - the functional component executes its render function directly to generate a VNode, - A normal component instantiates a VNode and sets four methods on its data.hook object. These methods are called during the patch phase of the component to instantiate, mount, and render the component */
vnode = createComponent(Ctor, data, context, children, tag)
} else {
// A tag that is not known, but also generated for VNode, because it is considered possible to give an appropriate namespace at runtime
// unknown or unlisted namespaced elements
// check at runtime because it may get assigned a namespace when its
// parent normalizes children
vnode = new VNode(
tag, data, children,
undefined.undefined, context
)
}
} else {
/* Tag is a non-string, such as a component's configuration object or a component's direct component options/constructor */
vnode = createComponent(tag, data, context, children)
}
// Returns the VNode of the component
if (Array.isArray(vnode)) {
return vnode
} else if (isDef(vnode)) {
if (isDef(ns)) applyNS(vnode, ns)
if (isDef(data)) registerDeepBindings(data)
return vnode
} else {
return createEmptyVNode()
}
}
Copy the code
The createComponent () method
File Location:src\core\vdom\create-component.js
/** * Create the VNode of the component, * 1, the functional component generates the VNode of the component by executing its render method * 2, the normal component generates its VNode by new VNode(), The data.hook object has four hook functions: init, prepatch, Insert, destroy. These hook functions are called during the patch phase of the component. When invoked, the child component instance is created and mounted until rendering is complete@param {*} Ctor component constructor *@param {*} The data property is a JSON string *@param {*} Context *@param {*} Children array of child nodes *@param {*} Tag Indicates the tag name *@returns VNode or Array<VNode>
*/
export function createComponent (
Ctor: Class<Component> | Function | Object | void, data: ? VNodeData, context: Component, children: ?Array<VNode>, tag? : string) :VNode | Array<VNode> | void {
/ / component constructor does not exist, directly to the end, Ctor = = = undefined | | Ctor = = = null
if (isUndef(Ctor)) {
return
}
// Vue.extend
const baseCtor = context.$options._base
// plain options object: turn it into a constructor
// When Ctor is a configuration object, it is converted to a constructor by vue.extend
if (isObject(Ctor)) {
Ctor = baseCtor.extend(Ctor)
}
// if at this stage it's not a constructor or an async component factory,
// reject.
// If Ctor is not a function at this point, it is an invalid component definition
if (typeofCtor ! = ='function') {
if(process.env.NODE_ENV ! = ='production') {
warn(`Invalid Component definition: The ${String(Ctor)}`, context)
}
return
}
// async Component -- Async component
let asyncFactory
if (isUndef(Ctor.cid)) {
asyncFactory = Ctor
Ctor = resolveAsyncComponent(asyncFactory, baseCtor)
if (Ctor === undefined) {
/* Returns a placeholder node for the asynchronous component, which is rendered as an annotation node but retains all the original information of the node, which will be used for asynchronous server rendering and hydration */
return createAsyncPlaceholder(
asyncFactory,
data,
context,
children,
tag
)
}
}
// Node properties JSON String
data = data || {}
/* This is where the component merges options, i.e. the compiler compiles the component into a render function, executes the render function, and then executes the _c in it. This is where the constructor options are parsed and the base class options are combined to prevent global mixing */ applied after the component constructor is created
resolveConstructorOptions(Ctor)
// transform component v-model data into props & events
// Convert the component's V-model information (values and callbacks) to attributes and values of the data.attrs object and events and callbacks on the data.on object
if (isDef(data.model)) {
transformModel(Ctor.options, data)
}
/* propsData[key] = val = value */; /* propsData[key] = val = value */
const propsData = extractPropsFromVNodeData(data, Ctor, tag)
// functional Component -- Functional component
if (isTrue(Ctor.options.functional)) {
Set the props object of the component. 2. Set the render context of the functional component and pass it to the render function of the functional component to generate the VNode
return createFunctionalComponent(Ctor, propsData, data, context, children)
}
// extract listeners, since these needs to be treated as
// child component listeners instead of DOM listeners
// Get the event listener object data.on, because these listeners need to be handled as child component listeners, not DOM listeners
const listeners = data.on
// replace with listeners with .native modifier
// so it gets processed during parent component patch.
// Assign the event object with the.native modifier to data.on
data.on = data.nativeOn
if (isTrue(Ctor.options.abstract)) {
// If it is an abstract component, the props, listeners, and slots are reserved
// abstract components do not keep anything
// other than props & listeners & slot
// work around flow
const slot = data.slot
data = {}
if (slot) {
data.slot = slot
}
}
Init, prepatch, insert, destroy; /** Create, update, destroy; /* create, update, destroy; Install Component management hooks onto the placeholder node */ install Component Management hooks onto the placeholder node */
installComponentHooks(data)
// return a placeholder vnode
const name = Ctor.options.name || tag
Vue -component-${cid}-${name}
const vnode = new VNode(
`vue-component-${Ctor.cid}${name ? ` -${name}` : ' '}`,
data, undefined.undefined.undefined, context,
{ Ctor, propsData, listeners, tag, children },
asyncFactory
)
// Weex specific: invoke recycle-list optimized @render function for
// extracting cell-slot template.
// https://github.com/Hanks10100/weex-native-directive/tree/master/component
/* istanbul ignore if */
if (__WEEX__ && isRecyclableComponent(vnode)) {
return renderRecyclableComponentTemplate(vnode)
}
return vnode
}
Copy the code
ResolveConstructorOptions () method
File Location:src\core\vdom\create-component.js
// Parse configuration objects from constructors
export function resolveConstructorOptions (Ctor: Class<Component>) {
let options = Ctor.options
// If the super property of the constructor is present, the base class is proved, which requires recursive processing
if (Ctor.super) {
const superOptions = resolveConstructorOptions(Ctor.super)
/ / cache
const cachedSuperOptions = Ctor.superOptions
// If the cache configuration is inconsistent with that of the base class, the configuration has changed
if(superOptions ! == cachedSuperOptions) {// super option changed,
// need to resolve new options.
Ctor.superOptions = superOptions
// check if there are any late-modified/attached options (#4976)
// Get the changed configuration item
const modifiedOptions = resolveModifiedOptions(Ctor)
// update base extend options
if (modifiedOptions) {
// Merge the changed configuration items with the extent option
extend(Ctor.extendOptions, modifiedOptions)
}
// Assign the new configuration after the merge to $options
options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
if (options.name) {
options.components[options.name] = Ctor
}
}
}
return options
}
Copy the code
ResolveModifiedOptions () method
File Location:/src/core/instance/init.js
/** * resolves subsequent changes or additions to the constructor option */
function resolveModifiedOptions (Ctor: Class<Component>): ?Object {
let modified
// Constructor options
const latest = Ctor.options
// Sealed constructor option, backup
const sealed = Ctor.sealedOptions
// Compare the two options and record the inconsistent options
for (const key in latest) {
if(latest[key] ! == sealed[key]) {if(! modified) modified = {} modified[key] = latest[key] } }return modified
}
Copy the code
TransformModel () method
File Location:/src/core/vdom/create-component.js
/** * Transform component V-model info (value and callback) to attributes and values of the data.attrs object and events and callbacks on the data.on object callback) into * prop and event handler respectively. */
function transformModel (options, data: any) {
// Attributes and events for model, default to value and input
const prop = (options.model && options.model.prop) || 'value'
const event = (options.model && options.model.event) || 'input'
// Store v-model values in the data.attrs object; (data.attrs || (data.attrs = {}))[prop] = data.model.value// Store v-model events on the data.on object
const on = data.on || (data.on = {})
// An existing event callback function
const existing = on[event]
// The event callback function in the V-model
const callback = data.model.callback
// merge callback functions
if (isDef(existing)) {
if (
Array.isArray(existing)
? existing.indexOf(callback) === -1: existing ! == callback ) { on[event] = [callback].concat(existing) } }else {
on[event] = callback
}
}
Copy the code
ExtractPropsFromVNodeData () method
File Location:/src/core/vdom/helpers/extract-props.js
/** *
When the data in the parent component is updated, a reactive update is triggered, render is re-executed, a new VNode is generated, and the corresponding data in the parent component is updated */
export function extractPropsFromVNodeData (data: VNodeData, Ctor: Class
, tag? : string
): ?Object {
// Props: {props: {MSG: {type: String, default: xx}}
// Only raw values are extracted here, validation and default values are handled in child components
// we are only extracting raw values here.
// validation and default values are handled in the child
// component itself.
const propOptions = Ctor.options.props
// Props is not defined
if (isUndef(propOptions)) {
return
}
/* When data in the parent component is updated, trigger a reactive update, re-execute render, generate a new VNode, and then the corresponding data in the component will be updated */
const res = {}
const { attrs, props } = data
if (isDef(attrs) || isDef(props)) {
/ / traverse propsOptions
for (const key in propOptions) {
// Convert the small hump key to a hyphen
const altKey = hyphenate(key)
/* If you want to declare props as a small hump (testProps), but because HTML is case insensitive, you should use test-props instead of testProps */
if(process.env.NODE_ENV ! = ='production') {
const keyInLowerCase = key.toLowerCase()
if( key ! == keyInLowerCase && attrs && hasOwn(attrs, keyInLowerCase) ) { tip(`Prop "${keyInLowerCase}" is passed to component ` +
`${formatComponentName(tag || Ctor)}, but the declared prop name is` +
`"${key}". ` +
`Note that HTML attributes are case-insensitive and camelCased ` +
`props need to use their kebab-case equivalents when using in-DOM ` +
`templates. You should probably use "${altKey}" instead of "${key}". `
)
}
}
checkProp(res, props, key, altKey, true) ||
checkProp(res, attrs, key, altKey, false)}}return res
}
Copy the code
CheckProp () method
File Location:/src/core/vdom/helpers/extract-props.js
// get res[key] = val
function checkProp (
res: Object,
hash: ?Object,
key: string,
altKey: string,
preserve: boolean
) :boolean {
if (isDef(hash)) {
/* If there is a key or altKey in the hash (props/attrs) object, set this parameter to res => res[key] = hash[key] */
if (hasOwn(hash, key)) {
res[key] = hash[key]
if(! preserve) {delete hash[key]
}
return true
} else if (hasOwn(hash, altKey)) {
res[key] = hash[altKey]
if(! preserve) {delete hash[altKey]
}
return true}}return false
}
Copy the code
CreateFunctionalComponent () method
File Location:/src/core/vdom/create-functional-component.js
/** * Execute the render function of the functional component to generate the VNode of the component: * 1, set the props object of the component * 2, set the render context of the functional component, pass to the render function of the functional component * 3, call the render function of the functional component to generate vNode * *@param {*} Ctor component constructor *@param {*} PropsData Additional props object *@param {*} JSON string * composed of data node attributes@param {*} ContextVm context *@param {*} Children array of child nodes *@returns Vnode or Array<VNode>
*/
export function createFunctionalComponent (
Ctor: Class<Component>,
propsData: ?Object,
data: VNodeData,
contextVm: Component,
children: ?Array<VNode>
) :VNode | Array<VNode> | void {
// Component configuration items
const options = Ctor.options
// Get the props object
const props = {}
// The props option for the component itself
const propOptions = options.props
// Sets the props object for the functional component
if (isDef(propOptions)) {
/* If the functional component provides props, set the props. Key value to the corresponding key */ passed from the component
for (const key in propOptions) {
props[key] = validateProp(key, propOptions, propsData || emptyObject)
}
} else {
// If the current functional component does not provide props, the attribute on the component is automatically resolved to props
if (isDef(data.attrs)) mergeProps(props, data.attrs)
if (isDef(data.props)) mergeProps(props, data.props)
}
// Instantiate the rendering context of a functional component
const renderContext = new FunctionalRenderContext(
data,
props,
children,
contextVm,
Ctor
)
// Call render function to generate vNode and pass _c and render context to render function
const vnode = options.render.call(null, renderContext._c, renderContext)
// Add some tags to the last generated VNode object to indicate that the VNode was generated by a functional component, and return the VNode
if (vnode instanceof VNode) {
return cloneAndMarkFunctionalResult(vnode, data, renderContext.parent, options, renderContext)
} else if (Array.isArray(vnode)) {
const vnodes = normalizeChildren(vnode) || []
const res = new Array(vnodes.length)
for (let i = 0; i < vnodes.length; i++) {
res[i] = cloneAndMarkFunctionalResult(vnodes[i], data, renderContext.parent, options, renderContext)
}
return res
}
}
Copy the code
InstallComponentHooks () method
File Location:/src/core/vdom/create-component.js
const hooksToMerge = Object.keys(componentVNodeHooks)
Init, prepatch, insert, destroy; / / Create, update, destroy; / / Create, update, destroy
function installComponentHooks (data: VNodeData) {
const hooks = data.hook || (data.hook = {})
// Walk through the hooksToMerge array, hooksToMerge = ['init', 'prepatch', 'insert' 'destroy']
for (let i = 0; i < hooksToMerge.length; i++) {
// key = init
const key = hooksToMerge[i]
// Get the method corresponding to the key from the data.hook object
const existing = hooks[key]
// Method for the key object in the componentVNodeHooks object
const toMerge = componentVNodeHooks[key]
// Merge the user-passed hook method with the framework's own hook method
if(existing ! == toMerge && ! (existing && existing._merged)) { hooks[key] = existing ? mergeHook(toMerge, existing) : toMerge } } }Copy the code
ComponentVNodeHooks object
File Location:/src/core/vdom/create-component.js
Invoked inline hooks on the component VNode to be invoked on component VNodes during patch */
const componentVNodeHooks = {
/ / initialization
init (vnode: VNodeWithData, hydrating: boolean): ? boolean {if( vnode.componentInstance && ! vnode.componentInstance._isDestroyed && vnode.data.keepAlive ) {/* Kept -alive components, treat as a patch */
const mountedNode: any = vnode // work around flow
componentVNodeHooks.prepatch(mountedNode, mountedNode)
} else {
/ / create the component instances, namely the new vnode.com ponentOptions. Ctor (options) = > get Vue component instance
const child = vnode.componentInstance = createComponentInstanceForVnode(
vnode,
activeInstance
)
/* Execute the component's $mount method to enter the mount phase, then get the render function through the compiler, and then go through the mount and patch process until the component renders to the page */
child.$mount(hydrating ? vnode.elm : undefined, hydrating)
}
},
// Update VNode, update various properties on the old VNode with the new VNode configuration
prepatch (oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) {
// Component configuration items for the new VNode
const options = vnode.componentOptions
// Component configuration items of the old VNode
const child = vnode.componentInstance = oldVnode.componentInstance
Update various attributes on child with attributes on vNode
updateChildComponent(
child,
options.propsData, // updated props
options.listeners, // updated listeners
vnode, // new parent vnode
options.children // new children)},// Execute the component's Mounted declaration cycle hook
insert (vnode: MountedComponentVNode) {
const { context, componentInstance } = vnode
// If the component is not mounted, call Mounted to declare the periodic hook
if(! componentInstance._isMounted) { componentInstance._isMounted =true
callHook(componentInstance, 'mounted')}// Handle the keep-alive exception
if (vnode.data.keepAlive) {
if (context._isMounted) {
// vue-router#1212
// During updates, a kept-alive component's child components may
// change, so directly walking the tree here may call activated hooks
// on incorrect children. Instead we push them into a queue which will
// be processed after the whole patch process ended.
queueActivatedComponent(componentInstance)
} else {
activateChildComponent(componentInstance, true /* direct */)}}},If the keep-alive component is wrapped, the component is deactivated and the instance is not destroyed, thus caching the state of the component. If the keep-alive component is not wrapped, the instance's $destroy method is called to destroy the component */
destroy (vnode: MountedComponentVNode) {
// Get the component instance from vNode
const { componentInstance } = vnode
// If the component instance is not destroyed
if(! componentInstance._isDestroyed) {// If the component is not wrapped by the keep-alive component, call $destroy directly to destroy the component
if(! vnode.data.keepAlive) { componentInstance.$destroy() }else {
// The component is wrapped by the keep-alive component, which deactivates the component without destroying the component instance, thus caching the state of the component
deactivateChildComponent(componentInstance, true /* direct */)}}}}Copy the code
CreateComponentInstanceForVnode object
File Location:/src/core/vdom/create-component.js
/ * new vnode.com ponentOptions Ctor (options) = > get Vue component instance * /
export function createComponentInstanceForVnode (
// Flow does not know that it is MountedComponentVNode
vnode: any,
// activeInstance in the lifecycle state
parent: any
) :Component {
const options: InternalComponentOptions = {
_isComponent: true._parentVnode: vnode,
parent
}
// Check the inline template rendering function
const inlineTemplate = vnode.data.inlineTemplate
if (isDef(inlineTemplate)) {
options.render = inlineTemplate.render
options.staticRenderFns = inlineTemplate.staticRenderFns
}
return new vnode.componentOptions.Ctor(options)
}
Copy the code
conclusion
How does a component become a VNode?
- Component instance
init
Initialize, and execute last$mount
The mount phase is displayed - If it is only containingThe runtime 的
vue.js
, only enter the mount phase directly, since the component has become a rendering function;The build processthroughModule packer + VUE-loader + Vue-template-compiler
To complete the - If not usedprecompiled, must be used in full
vue.js
- If the component configuration item does not exist during mounting
render
Option, enterCompilation phase - Compile the template string to
AST
Syntax tree – PlainJS
object - Then the optimization
AST
To traverse theAST
Object that marks whether each node isStatic node; And then we can label it furtherStatic root nodeThese static node updates are skipped in subsequent component updates to improve performance - The following from
AST
generateRendering function :- Responsible for generating dynamic nodes
VNode
的render
function staticRenderFns
Array, where each element is a generated static nodeVNode
Functions of phi, and these functions will act asrender
Function that is responsible for generating static nodesVNode
- Responsible for generating dynamic nodes
- Next place the render function on the component’s configuration object and enterMount the stageThat perform
mountComponent
methods - Ultimately responsible for rendering and updating components is a person called
updateComponent
Method that needs to be executed before each executionvm._render
Function that is responsible for executing the compiler generatedrender
And get the componentVNode
- Generating components
VNode
The specific work is made up ofrender
In the function_c, _O, _L, _m
All of these methods are mounted toVue
Instance, responsible for generating components at run timeVNode
- Set the component configuration information and pass
New VNode(Component information)
Component-generatingVNode
The Render Helper is used to mount runtime utility methods on the Vue instance that are used in the compiler-generated rendering functions to generate the component’s VNodes
What is the principle of the props response?
- In the process of component
props
, the component is extractedprops
Data to componentprops
The attribute in the configuration iskey
, the corresponding data in the parent component isvalue
To generate apropsData
object - New ones are generated when components are updated
VNode
, and then go to the previous step and get the updatedpropsData
object
The articles
- What does vUE initialization do?
- How to understand vUE responsiveness?
- How does vUE update asynchronously?
- Do you really understand the Vue global Api?
- Did you lose the instance method in VUE?
- Do you know Hook Event?
- Vue compiler parsing
- Optimization of vUE compiler parsing
- How does the VUE compiler generate a rendering function?