$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