$mount templateparse

$mount Template compilation –optimize

$mount template: generate

Template –> AST –> render function (template –> AST –> render function) This article we mainly introduce the generation process of VNode, before this, we first to a simple understanding of what is VNode?

First take a look at the Vue. Js source code for the VNode class definition.

src/core/vdom/vnode.js
Copy the code
exportdefault class VNode { tag: string | void; data: VNodeData | void; children: ? Array<VNode>; text: string | void; elm: Node | void; ns: string | void; context: Component | void; // renderedin this component's scope key: string | number | void; componentOptions: VNodeComponentOptions | void; componentInstance: Component | void; // component instance parent: VNode | void; // component placeholder node // strictly internal raw: boolean; // contains raw HTML? (server only) isStatic: boolean; // hoisted static node isRootInsert: boolean; // necessary for enter transition check isComment: boolean; // empty comment placeholder? isCloned: boolean; // is a cloned node? isOnce: boolean; // is a v-once node? asyncFactory: Function | void; // async component factory function asyncMeta: Object | void; isAsyncPlaceholder: boolean; ssrContext: Object | void; fnContext: Component | void; // real context vm for functional nodes fnOptions: ? ComponentOptions; // for SSR caching fnScopeId: ? string; // functional scope id support constructor ( tag? : string, data? : VNodeData, children? :? Array
      
       , text? : string, elm? : Node, context? : Component, componentOptions? : VNodeComponentOptions, asyncFactory? : Function) {/* This. Tag = tag /* This. */ this.data = data /* Child nodes of the current node, Is an array */ this.children = children /* the text of the current node */ this.text = text /* the real DOM node corresponding to the current virtual node */ this.elm = elm /* the namespace of the current node */ this.ns This. FunctionalContext = undefined /* compiler scope */ this.context = context /* functional component scope */ this.functionalContext = undefined /* key property of the node, used as a node flag, To optimize */ this.key = data && data.key /* Component's option option */ this.componentOptions = componentOptions /* Instance of the component corresponding to the current node */ This.parent = undefined /* The parent node of the current node */ this.parent = undefined /* TextContent is false*/ this.raw = false /* Static node flag */ this.isStatic = false /* Whether to insert with node */ this.isRootInsert = true /* Whether there is a annotation node */ this.iscomment = false /* Whether there is a clone node */ this.isonce = false /* Whether there is a V-once instruction */ this.isonce = false /* Factory method of asynchronous components */ This. asyncFactory = asyncFactory /* Async source */ this.asyncMeta = undefined /* Async preassignment */ this.isAsyncPlaceholder = false} // DEPRECATED: alias for componentInstance for backwards compat. /* istanbul ignore next */ get child (): Component | void { return this.componentInstance } }
      Copy the code

This is a basic VNode node that serves as a base class for other derived VNode classes and defines the following data.

Tag: indicates the tag name of the current node

Data: indicates the object corresponding to the current node, which contains specific data information. It is of the VNodeData type. For details, see the VNodeData type

Children: An array of children of the current node

Text: indicates the text of the current node

Elm: real DOM node corresponding to the current virtual node

Ns: indicates the namespace of the current node

Context: compilation scope of the current node

FunctionalContext: Functional component scope

Key: The key attribute of a node, which is used as a node identifier for optimization

ComponentOptions: Indicates the option of a component

ComponentInstance: componentInstance corresponding to the current node

Parent: indicates the parent of the current node

Raw: Simply means whether it is native HTML or plain text. True for innerHTML and false for textContent

IsStatic: indicates whether the node isStatic

IsRootInsert: Indicates whether to be inserted as the heel node

IsComment: Indicates whether it is a comment node

Isempirical: Whether there is a clone node

IsOnce: indicates whether the V-once command exists

AsyncFactory: Factory method for asynchronous components

AsyncMeta: asynchronous source

IsAsyncPlaceholder: Preassignment with or without asyncplaceholder

VNode contains attributes that we commonly use to mark nodes. Why VNode? In fact, there is a lot of information online can understand the advantages of VNode, such as a large number of sub-interactive performance advantages, SSR support, cross-platform Weex support… I won’t repeat it here. Let’s look at the basic classification of vNodes:

Let’s start with an example:

{
    tag: 'div'
    data: {
        class: 'test'
    },
    children: [
        {
            tag: 'span',
            data: {
                class: 'demo'
            }
            text: 'hello,VNode'}}]Copy the code

This is what the rendering would look like

<div class="test">
    <span class="demo">hello,VNode</span>
</div>
Copy the code

Generate VNode

Armed with the knowledge from the previous section, we now need to compile the following template

<div id="app">
  <header>
    <h1>I'm a template!   

{{ message }}

No message.

Copy the code

Get the render function that looks like this

(function() {
  with(this){
    return _c('div',{// Create a div element attrs:{"id":"app"} //div add attribute id},[_m(0), // static node header, corresponding to render with staticRenderFns array index 0function
        _v(""), // Empty text node (message) // triadic expression to determine whether message exists // If so, create a p element with text in it and value toString(message). _c('p',[_v("\n "+_s(message)+"\n ")]) // If it does not exist, create a p element with text and value No message. :_c('p',[_v("\n No message.\n ")]])}})Copy the code

Render function _c, _m, _v, _s render function _c is createElement, _m is renderStatic, _v is createTextVNode _s is toString. We call the vm._render() method during compilation, where the _render function has the following statement:

const { render, _parentVnode } = vm.$options. vnode = render.call(vm._renderProxy, vm.$createElement)
Copy the code

Next, we will systematically talk about how to create a Vnode.

Method to generate a new VNode

CreateEmptyVNode Creates an empty VNode

/* Create an empty VNode */export const createEmptyVNode = () => {
  const node = new VNode()
  node.text = ' '
  node.isComment = true
  return node
}
Copy the code

CreateTextVNode Creates a text node

/* Create a text node */export function createTextVNode (val: string | number) {
  return new VNode(undefined, undefined, undefined, String(val))
}
Copy the code

CreateComponent Creates a component node

src/core/vdom/create-element.js
Copy the code
// wrapper function for providing a more flexible interface
// without getting yelled at by flow
export functioncreateElement ( context: Component, tag: any, data: any, children: any, normalizationType: any, alwaysNormalize: Boolean) : VNode | Array < VNode > {/ * * / compatible not sending dataif(Array.isArray(data) || isPrimitive(data)) { normalizationType = children children = data data = undefined } /* If alwaysNormalize istrue, normalizationType is marked as ALWAYS_NORMALIZE*/if(isTrue(alwaysNormalize)) {normalizationType = ALWAYS_NORMALIZE} /* Create virtual node */return_createElement(context, tag, data, children, normalizationType)} /* Create a virtual node */export function_createElement ( context: Component, tag? : string | Class<Component> | Function | Object, data? : VNodeData, children? : any, normalizationType? : number ): VNode | Array < VNode > {/ * if transfer data parameters and data of __ob__ already defined above (representative has been observed, binding the Oberver object).  https://cn.vuejs.org/v2/guide/render-function.html# constraintsCreate an empty node */if(isDef(data) && isDef((data: any).__ob__)) { process.env.NODE_ENV ! = ='production' && warn(
      `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` +
      'Always create fresh vnode data objects in each render! ',
      context
    )
    return createEmptyVNode()
  }
  // object syntax in v-bind
  if(isDef(data) &&isdef (data.is)) {tag = data.is} /* If the tag does not exist, create an empty node */if(! tag) { //in case of component :is set to falsy value
    return createEmptyVNode()
  }
  // warn against non-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
      )
    }
  }
  // support single functionChildren as default scoped slot /* Default scope slot */if (Array.isArray(children) &&
    typeof children[0] === 'function'
  ) {
    data = data || {}
    data.scopedSlots = { default: children[0] }
    children.length = 0
  }
  if (normalizationType === ALWAYS_NORMALIZE) {
    children = normalizeChildren(children)
  } else if (normalizationType === SIMPLE_NORMALIZE) {
    children = simpleNormalizeChildren(children)
  }
  let vnode, ns
  if (typeof tag === 'string') {
    letCtor /* Gets the namespace of the tag */ ns = (context).$vnode && context.$vnodeNs) | | config. GetTagNamespace (tag) / * judge whether to retain the label * /if(config.isreservedTag (tag)) {// platform built-in elements /* Create a node if the tag is reserved */ vnode = new vnode ( config.parsePlatformTagName(tag), data, children, undefined, undefined, context ) }else if (isDef(Ctor = resolveAsset(context.$options.'components', tag))) {// component /* Find the tag in the option components of the VM instance. */ vnode = createComponent(Ctor, data, context, children, tag)}else{ // unknown or unlisted namespaced elements // check at runtime because it may get assigned a namespace when its // Parent normalizing children /* the unknown elements, checking at run time, */ vnode = new vnode (tag, data, children, undefined, undefined, context)}}else{// direct component options/constructor /* Vnode = createComponent(tag, data, context, etc.) children) }if (Array.isArray(vnode)) {
    return vnode
  } else if(isDef(vnode)) {/* If there is a namespace, recursively all child nodes apply that namespace */if (isDef(ns)) applyNS(vnode, ns)
    if (isDef(data)) registerDeepBindings(data)
    return vnode
  } else{/* Create an empty node */ if the vNode is not created successfullyreturn createEmptyVNode()
  }
}
Copy the code

CreateElement is used to create a virtual node. When __ob__ is bound to data, it means that the object has already been Oberver, so an empty node is created. An empty node is also created if the tag does not exist. When a tag is not a String, it means that the tag is a component constructor class, which is created directly with new VNode. If the tag is a String, create a VNode instance using new VNode if the tag is reserved. If the tag can be found in the OPTION components of the VM, it indicates that the VNode is a component. Otherwise, create a VNode instance using new VNode.

If you like, give me a star, Github