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 instanceinitInitialize, and execute last$mountThe mount phase is displayed
  • If it is only containingThe runtimevue.js, only enter the mount phase directly, since the component has become a rendering function;The build processthroughModule packer + VUE-loader + Vue-template-compilerTo complete the
  • If not usedprecompiled, must be used in fullvue.js
  • If the component configuration item does not exist during mountingrenderOption, enterCompilation phase
  • Compile the template string toASTSyntax tree – PlainJSobject
  • Then the optimizationASTTo traverse theASTObject 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 fromASTgenerateRendering function
    • Responsible for generating dynamic nodesVNoderenderfunction
    • staticRenderFnsArray, where each element is a generated static nodeVNodeFunctions of phi, and these functions will act asrenderFunction that is responsible for generating static nodesVNode
  • Next place the render function on the component’s configuration object and enterMount the stageThat performmountComponentmethods
  • Ultimately responsible for rendering and updating components is a person calledupdateComponentMethod that needs to be executed before each executionvm._renderFunction that is responsible for executing the compiler generatedrenderAnd get the componentVNode
  • Generating componentsVNodeThe specific work is made up ofrenderIn the function_c, _O, _L, _mAll of these methods are mounted toVueInstance, responsible for generating components at run timeVNode
  • Set the component configuration information and passNew 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 componentprops, the component is extractedpropsData to componentpropsThe attribute in the configuration iskey, the corresponding data in the parent component isvalueTo generate apropsDataobject
  • New ones are generated when components are updatedVNode, and then go to the previous step and get the updatedpropsDataobject

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?