render
Vue’s _render method is a private method of the instance that renders it as a virtual node. He defined in SRC/core/instance/render. The js file
Vue.prototype._render = function () :VNode {
const vm: Component = this
const { render, _parentVnode } = vm.$options
if (_parentVnode) {
vm.$scopedSlots = normalizeScopedSlots(
_parentVnode.data.scopedSlots,
vm.$slots,
vm.$scopedSlots
)
}
// set parent vnode. this allows render functions to have access
// to the data on the placeholder node.
vm.$vnode = _parentVnode
// render self
let vnode
try {
// There's no need to maintain a stack because all render fns are called
// separately from one another. Nested component's render fns are called
// when parent component is patched.
currentRenderingInstance = vm
vnode = render.call(vm._renderProxy, vm.$createElement)
} catch (e) {
handleError(e, vm, `render`)
// return error render result,
// or previous vnode to prevent render error causing blank component
/* istanbul ignore else */
if(process.env.NODE_ENV ! = ='production' && vm.$options.renderError) {
try {
vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
} catch (e) {
handleError(e, vm, `renderError`)
vnode = vm._vnode
}
} else {
vnode = vm._vnode
}
} finally {
currentRenderingInstance = null
}
// if the returned array contains only a single node, allow it
if (Array.isArray(vnode) && vnode.length === 1) {
vnode = vnode[0]}// return empty vnode in case the render function errored out
if(! (vnodeinstanceof VNode)) {
if(process.env.NODE_ENV ! = ='production' && Array.isArray(vnode)) {
warn(
'Multiple root nodes returned from render function. Render function ' +
'should return a single root node.',
vm
)
}
vnode = createEmptyVNode()
}
// set parent
vnode.parent = _parentVnode
return vnode
}
Copy the code
The most critical part of this code is the call of the Render method, we in the usual development work handwritten render method scene is less, and write more is the template template, in the previous mounted method implementation, template will be compiled into the render method, But this compilation process is very complicated.
The first argument to the render function is createElement, as described in the official Vue documentation.
<div id='app'>
{{message}}
</div>
Copy the code
Equivalent to writing the render function as follows:
render:function (createElement) {
return createElement('div', {
attrs: {
id: "app"}},this.message)
}
Copy the code
Call the render method in the _render function:
vnode = render.call(vm._renderProxy, vm.$createElement)
Copy the code
$createElement vm.$createElement vm.$createElement vm.
export function initRender (vm: Component) {
vm._vnode = null // the root of the child tree
vm._staticTrees = null // v-once cached trees
const options = vm.$options
const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree
const renderContext = parentVnode && parentVnode.context
vm.$slots = resolveSlots(options._renderChildren, renderContext)
vm.$scopedSlots = emptyObject
// bind the createElement fn to this instance
// so that we get proper render context inside it.
// args order: tag, data, children, normalizationType, alwaysNormalize
// internal version is used by render functions compiled from templates
vm._c = (a, b, c, d) = > createElement(vm, a, b, c, d, false)
// normalization is always applied for the public version, used in
// user-written render functions.
vm.$createElement = (a, b, c, d) = > createElement(vm, a, b, c, d, true)
// $attrs & $listeners are exposed for easier HOC creation.
// they need to be reactive so that HOCs using them are always updated
const parentData = parentVnode && parentVnode.data
/* istanbul ignore else */
if(process.env.NODE_ENV ! = ='production') {
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () = > {
!isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
}, true)
defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () = > {
!isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
}, true)}else {
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null.true)
defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null.true)}}Copy the code
In fact, the vm. CreateElement method is defined when the initRender method is executed, and you can see that except for the vm. CreateElement method is defined when the initRender method is executed, In addition to the vm.createElement method defined when executing the initRender method, you can see that in addition to the vm.createElement method, there is also a vm._c method, which is used by the render function compiled from the template. While vm.$createElement is used by the user’s handwritten Render method, the two methods support the same parameters and both call createElement internally.
conclusion
Vm. _render finally returns vNode, which is a virtual Node, by executing the createElement method. The biggest upgrade to Vue2.0 is VirtualDOM, so let’s look at the concept of VirtualDOM before we analyze the implementation of createElement
Virtual DOM
The concept of Virtual DOM is familiar to most people. The premise is that the DOM in the browser is very expensive. For a more intuitive feeling, we can simply print out the attributes of a simple DIV, as shown in the figure below:
As you can see, the actual DOM elements are very large, because browser standards make the DOM very complex. When we do frequent DOM updates, there will be certain performance issues.
VirtualDOM uses a native JS object to describe a DOM node, so it is much less expensive than creating a DOM. In Vue. Js, VirtualDOM is bought with a Class called VNode, which is defined in SRC /core/vdom/vnode.js
export default class VNode {
tag: string | void;
data: VNodeData | void;
children: ?Array<VNode>;
text: string | void;
elm: Node | void;
ns: string | void;
context: Component | void; // rendered in 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 nodesfnOptions: ? ComponentOptions;// for SSR caching
devtoolsMeta: ?Object; // used to store functional render context for devtoolsfnScopeId: ? string;// functional scope id support
constructor (tag? : string, data? : VNodeData, children? :?Array<VNode>, text? : string, elm? : Node, context? : Component, componentOptions? : VNodeComponentOptions, asyncFactory? :Function
) {
this.tag = tag
this.data = data
this.children = children
this.text = text
this.elm = elm
this.ns = undefined
this.context = context
this.fnContext = undefined
this.fnOptions = undefined
this.fnScopeId = undefined
this.key = data && data.key
this.componentOptions = componentOptions
this.componentInstance = undefined
this.parent = undefined
this.raw = false
this.isStatic = false
this.isRootInsert = true
this.isComment = false
this.isCloned = false
this.isOnce = false
this.asyncFactory = asyncFactory
this.asyncMeta = undefined
this.isAsyncPlaceholder = false
}
// DEPRECATED: alias for componentInstance for backwards compat.
/* istanbul ignore next */
get child (): Component | void {
return this.componentInstance
}
}
Copy the code
As you can see, the definition of VirtualDOM in vue.js is a little more complicated because it contains many of the features of vue.js. VirtualDOM in Vue. Js is actually a VirtualDOM implementation borrowed from the open source library Snabbdom, and then added some features of Vue. I recommend that you read the source code of this library if you want to know more about VirtualDOM of vue.js, because it is much simpler and more pure.
conclusion
In fact, VNode is an abstract description of the real DOM. Its core definition is nothing more than a few key attributes, such as tag name, data, child nodes, key values, etc. In fact, attributes are used to extend the interest of VNode and achieve some special features. Since VNode is only used to map renderings of the real DOM and does not need to include methods to manipulate the DOM, it is very lightweight and simple.
In addition to the definition of Virtual DOM’s data structure, the mapping to the real DOM actually goes through the process of VNode create, diff, patch and so on. In vue.js, the VNode create is created using the createElement method mentioned earlier.