Vue internal rendering view

1. What is a virtual DOM

  • M is imperative previous operation i.e. with jQuery DOM DOM node operation, with the increase of the state, the operation will be more DOM increasingly frequent, the state program is also more difficult to maintain, and now the mainstream framework are based on declarative manipulate the DOM, DOM method of operation will encapsulate, as long as we change the state of the data, the framework itself will help us operate DOM.
  • The virtual state to establish a virtual DOM node tree, new virtual node tree will be the old virtual node tree contrast, rendering only partially changed, as shown below:


2. the purpose of introducing virtual DOM is

  • the rendering process abstraction, so that the abstraction component has also improved, and may be adapted render target other than the DOM;
  • can better support the SSR, rendering isomorphism;
  • no longer dependent HTML parser parses the template, can be performed more the AOT (pre compile) work to improve the run-time efficiency, but also will further compress the volume Vue runtime.

defined in the definition Vue VNode vnode’s constructor, we instantiate the various instances of vnode such as: text nodes, comment nodes and element nodes and the like.

  var VNode = function VNode (tag, data, children, text, elm, context, componentOptions, asyncFactory) {this.tag = tag; = 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;};  
vnode is actually an object describing nodes, describing how to create a real DOM node; VNODE’s role is the new old vNode comparison , Only update the changed node. VNode has an annotation node, text node, element node, component node, function component, clon node:

Var createemptyvnode = function (text) {if (text === Void 0) text = ”; var node = new vNode (); node.text = text;Node.iscomment = true; return node};
Only ISComment and Text properties are valid, the rest of the default is false or NULL
   text node 

Function CreateTextVNode (VAL) {Return New VNode (undefined, undefined, undefined, string (val))}


function clonevnode (vNode) {var cloned = new vNode VNODE.TAG, VNODE.DATA, / / ​​# 7975 // Clone Children Array To Avoid Mutating Original In Case Of Cloning // A Child. vnode.children && vnode.children.slice (), vnode.text, vnode.ext, VNode.ComponentOptions, vNode.asyncFactory; cloned.ns = vnode.ns; cloned.isstatic = vnode.isstatic; cloned.key = vnode.ys; cloned.iscomment = vnode.iscomment; cloned.fncontext = vnode.fnContext; cloned.fnOptions = vnode.fnOptions; cloned.fnScopeId = vnode.fnScopeId; cloned.asyncMeta = vnode.asyncMeta; cloned.isCloned = true; return cloned}

Clone node assigns all attributes of the vNode to the Clone node and sets iscloned = true, which is to optimize static nodes and slot nodes. Take a static node as an example, because the content of the static node does not change, when it first generates a virtual DOM node, it is not necessary to generate vnode again, but a rendering of the original VNODE clone, so that Experience improves performance.
Element node element node generally there will be four valid properties of Tag, Data, Children, Context, such as:
{Children: [vNode, vNode ], context: {…}, tag: ‘div’, data: {attr: {id: app}}}

Component node component node has two Unique attributes (1) ComponentOptions, component nodes option parameters, including the following:
   {CTOR: CTOR, PropsData: PropsData, Listeners: Listener, Tag: tag, children : Children} 
(2) ComponentInstance: instance of components, is also an instance of VUECorresponding vNode

New vNode (“Vue-Component-” + (CTOR.CID) + (Name? (“-” + Name): ‘)) , Data, undefined, undefined, undefined, context, {CTOR: CTOR, PROPSDATA: PROPSDATA, LISTENERS: LISTENERS, TAG: TAG, Children: Children}, asyncfactory
That is,

{ComponentOption: {}, componentInstance: {}, tag: ‘Vue-Component-1-Child’, Data: {…}, … }
  The function component function component is created by  CreateFunctionalComponent 
function, similar to the component node, there is no special attributes for the time being, and if there is a reinforcement superior.

The most important function of virtual DOM is Patch, rendering VNODE as a real DOM.
Patch Introduction
   Patch Chinese means that the patch is modified on the original basis, or it is also a rendering view. There are three modifications to the DOM node: 
Creating a new node

Delete the discarded node

Modify the node that needs to be updated.

When the cached last OldVNODE When the latest vnode is inconsistent, the rendering view is VNODE.

When there is no OldVNode, when the vNode exists, it is necessary to use VNODE to generate a real DOM node and inserted into the view. First, if the vNode has a Tag property, it is considered that it is an element attribute, and then the real element node is created according to the current environment, and the element is created into the specified parent node. The above-made VNODE as an example, the first execution

  • VM._UPDATE (VM._Render (), hydrating);
  • VM._Render () is the VNODE generated by the last piece, _Update function is specifically

Vue.Prototype._Update = function (vNode, hydrating) {var VM = this;. var prevEl = vm $ el; var prevVnode = vm._vnode; var restoreActiveInstance = setActiveInstance (vm); // buffer vnode vm._vnode = vnode; // Vue.prototype .__ patch__ is injected in entry points // based On The Rendering Backend Used. // For the first rendering, PrevNode is an IF (! prevnode) {// Initial Render VM. $ EL = VM .__ Patch__ (VM. $ EL, VNODE, HYDRATING, FALSE / * REMOVEONLy * /);} else {// updates VM. $ EL = VM .__ Patch __ (prevvnode, vNode);} restoreActiveInstance (); // update __vue__10 if (prevel) {prevel .__ vue__ = null;} if (VM $ EL) {VM. $ EL .__ VUE__ = VM;} // if Parent IS An Hoc, Update ITS $ EL AS Well IF (VM. $ VNODE && VM. $ PARENT && VM. $ VNODE === VM. $ PARENT._VNODE) ​​{VM. $ PARENT. $ EL = VM. $ EL;} // updated hook is called by the scheduler to ensure That children is Updated hook.};

Due to the first rendering, VM. $ EL = VM .__ Patch __ (VM. $ EL, VNODE, HYDRATING, FALSE / * Removeonly * /);
, Note that the first parameter is OldVNode as  VM. $ EL  is element node, __ patch__ function The specific process is: 
(1) Judgment whether OldVNode exists, there is no VNODE

IF (isundef (Oldvnode)) {// Empty Mount (Likey As ComponenT), Create New Root Element isinitialPatch = true; cretelm (vNode, INSERTEDVNODEQUE);}
   (2) There is an ELSE to determine if the OldVNode is an element node, if OldVNode It is an element node, then 

IF (isRelelement) {... // Either not server-rendered, or hydration failed. // create an empty node and replace it's Oldvnode = EmptyNodeat (OldVNode);} Create an OldVNode node, which is

{asyncFactory: undefined, asyncMeta: undefined, children: [], componentInstance: undefined, componentOptions: undefined, context: undefined, data: {}, elm: div # app, fnContext: undefined, fnOptions: undefined, fnScopeId: undefined, isAsyncPlaceholder: false, isCloned: False, ISComment: False, ISONCE: FALSE, ISROOTINSERT: TRUE, ISSTATIC: FALSE, KEY: UNDEfined, NS: undefined, parent: undefined, raw: false, tag: “div”, text: undefined, child: undefined}
then acquire an element node of OldVNode and Its parent node, and creates new nodes

// Replacing existing elementvar = oldvnode.elm; var parentelm = nodeops.parentNode (Oldelm); // Create New NodecreateElm (vnode, insertedVnodeQueue, // extremely rare edge case:.. do not insert if old element is in a // leaving transition Only happens when combining transition + // keep-alive + HOCs (# 4590) oldElm._leaveCb null?: Parentelm, nodeops.nextsibling (Oldelm);
  The process of creating a new node  

/ / Mark whether it is a root node vnode.isrootinsert =! Nested; // for transition enter check // This function If the vNode has the ComponentInstance property, the sub-component will be created, and the subsequent specifically introduced, otherwise IF (CREA)TECMOMPONENT (VNODE, INSERTEDVNODEQUE, PARENTELM, REFELM)) {Return}
  Connected to sub-node  

VAR DATA =; var children = vnode.children; var tag = vnode.tag; if (isdef (tag)) {… vnode.elm = vnode.ns? Nodeops.createElementns vnode.ns, tag): nodeOps.createElement (tag, vnode); setScope (vnode); / * istanbul ignore if * / {createChildren (vnode, children, insertedVnodeQueue); if (isDef (data)) {invokeCreateHooks (vnode, INSERTEDVNODEQUEUE);} Insert (Parentelm, vNode.elm, Refelm);}}}} (data && Data.pre) {CREATINGELMINVPRE —;}}}

  Put VNODE The property is set to create an element node ELEM, create a child node  CreateChildren (vNode, children, insertedvnodeQueue); 
This function passes the Chinese child node Children array

functiOn CreateChildren (vNode, Children, InsertvNodeQueue) {if (array.isaray (children)) {for (var i = 0; i

Traversing Children, recursive CreateElM method creation Child element node
Else IF (istrue (vNode.iscomment) {vNode.elm = nodeops.createcomment; insert (Parentelm, vNode.elm, Refelm);} else {vNode.elm = nodeops.createtextNode (vNode.text); Insert (Parentelm, vNode.elm, Refelm);}

If it is a comment node Create a comment node directly and insert it into the parent node, other creation text nodes, and insert it into the parent Parentelm. Trigger the hook, update the node properties, insert it into Parentelm The ‘#App’ Element Node)

{CreateChildren (vNode, Children, InsertedVNodeQueue; if (ISDEF (Data) {InvokecreateHooks (vNode, InsertEdequeue);} insert (Parentelm, vnode.elm, refelm);}
  Finally, delete the old node  
IF (ISDEF (Parentelm)) {RemoveVNodes (Parentelm, [OldvNode], 0, 0);} else if (ilvnode.tag)) {invokeStroyHook (OldVNode);}

Function RemoveandInvokeRemoveHook (vNode, RM) {IF (ISDEF (RM) || ISDEF (vNode.Data)) {var i; var Listeners = Cbs.remove.Length + 1; … // Recursively Invoke Hooks on child Component Root Node if (ISDEF (i = vnode.componentinstance) && isdef (i = i._vnode) && isdef ( {REMOVEANDINVOKEREMOVEHOKEK (I, RM);} for (i = 0; i
)  < children.length; ++i) {
   createElm(children[i], insertedVnodeQueue, vnode.elm, null, true, children, i);
 } else if (isPrimitive(vnode.text)) {
  // 如果vnode是文本直接挂载
  nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(String(vnode.text)));

Update node process

In order to better test, the template selection

  Click the button, update Message, re-rendering the view, generated vNode is  

{askNCFAACTORY: undefined, asyncMeta: undefined, children: [VNode, VNode], componentInstance: undefined, componentOptions: undefined, context: Vue example, data: {attrs: {id: “app”}}, elm: undefined, fnContext: undefined, fnOptions : undefined, fnScopeId: undefined, isAsyncPlaceholder: false, isCloned: false, isComment: false, isOnce: false, isRootInsert: true, isStatic: false, key: undefined, ns: undefined, parent: undefined, raw: false, tag: ” Div “, Text: undefined, child: undefined}

When the component is updated, prevnode and vnode exist, execution
   VM. $ EL = VM .__ Patch __ (prevvnode, vNode); 
In fact, it is actually running the following function
 PatchvNode (OldvNode, vNode, InsertedvNodeQueue, Null, Null, RemoveOnly);  < cbs.remove.length; ++i) {
   cbs.remove[i](vnode, rm);
  if (isDef(i = && isDef(i = i.remove)) {
   i(vnode, rm);
  } else {
   // 删除id为app的老节点
 } else {
This function first determines OldVNode and VNODWhether is equal, equal to


If Both are both static nodes and the key value is equal, and the vNode is cloned or isonce property, and the component instance of VNODE is directly assigned
IF (istrue. isStatic) && isTrue (oldVnode.isStatic) && vnode.key === oldVnode.key && (isTrue (vnode.isCloned) || isTrue (vnode.isOnce))) {vnode.componentInstance = oldVnode.componentInstance; return}
Next, the attribute value of the two is compared, and

var OldCH = OldVNode.Children; var ch = vNode.children; if (ISDEF (data) && ispatchable (vNode)) {for (i = 0; i
   comparison of vnode and OldVNODE and corresponding DOM operation specific As shown in: 
// vNode does not exist if the text attribute is IF (ISDEF (ISDEF (Oldch) && isdef (ch)) {/ When the child node is unequal, update if (Oldch! == CH) {UpdateChildren (ELM, Oldch, CH, INSERTEDVNODEQUE, REMOVEONLY);}}}}}}}}}}}} Else IF (ISDEF (CH)) {{checkduplicateKeys (ch);} // only the child node of VNODE, if OldVNode exists, there is a text property, Then empty the text content of the element, and add the ELM node if (ISDEF (Oldvnode.Text)) {nodeops.setTextContent (ELM, ”);} Addvnodes (Elm, Null, CH, 0, Ch.Length – 1, INSERTEDVNODEQUE ELSE IF (ISDEF (Oldch)) {// If there is only the child node of OldVNode, remove the child’s child node RemovevNodes (ELM, Oldch, 0, Oldch.length – 1);} else if (ISDEF Oldvnode.text) {// Only OldVNode has a text property, empty nodeops.setTextContent (ELM, ‘);}}}}}}}}}}}}}}}}}}} else.text! == vnode.text) {// Node and When the TEXT attribute of OldVNode exists and inconsistent, the element node content is set to vNode.text nodeops.SettextContent (ELM, vNode.text);}

Comparison of child nodes , First define two pointer indexes of OldVNode and VNODE two arrays
VAR OldStartIDX= 0; var newStartIdx = 0; var oldEndIdx = oldCh.length – 1; var oldStartVnode = oldCh [0]; var oldEndVnode = oldCh [oldEndIdx]; var newEndIdx = newCh.length – 1; var newStartVnode = newCh [0]; Var newndvnode = newch [newndidx]; var ly, vnodx, idxinold, vnodetomove, refelm;

as shown below:

  Next is a while loop, in this process, oldstartidx, newstartidx, oldndidx, and newndidx will gradually close to the middle  

While (OldStartIDX

  When OldStartVNode or OldendVNode is empty, two intermediate move  

IF (iF (OldStartVNode)) {OldStartVNode = OldCh [++ OldStartIDX]; // vNode Has Been Moved Left} else f (iondef (OldendvNode)) {OldendVNode = Oldch [- OldendIDX];}

Next this piece, is OldStartIDX , NewStartidx, Oldendidx, and NewendIdx pairwise alignments process, a total of four kinds:
else if (sameVnode (oldStartVnode, newStartVnode)) {patchVnode (oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx) ; oldStartVnode = oldCh [++ oldStartIdx]; newStartVnode = newCh [++ newStartIdx];} else if (sameVnode (oldEndVnode, newEndVnode)) {patchVnode (oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx); oldEndVnode = oldCh [- oldEndIdx]; newEndVnode = newCh [- newEndIdx];} else if (sameVnode (oldStartVnode, newEndVnode)) {// vnode moved right patchVnode (oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx); canMove && nodeOps.insertBefore (parentElm, OldStartVNode.elm, Nodeops.nextsibling (OldStartVNode.extSibling); OldStartVNode = Oldch [++ OldStartIDX]; newndvnode = newch [- newndIDX];} else if (Samevnode (oldEndVnode, newStartVnode)) {// Vnode moved left patchVnode (oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx); canMove && nodeOps.insertBefore (parentElm, oldEndVnode.elm, oldStartVnode.elm); oldEndVnode = oldCh [- oldEndIdx ]; newstartvnode = newch [++ newstartidx];}
First type: prior to equal comparison

 If it is equal,  OldStartVNode.elm and NewStartVNode.elm < cbs.update.length; ++i) {  // 以vnode为准更新oldVnode的不同属性
  cbs.update[i](oldVnode, vnode); 
 if (isDef(i = data.hook) && isDef(i = i.update)) { 
  i(oldVnode, vnode); 
} are moved backward, continue to compare. Second: Next, equal comparison 


newndvnode.elm One, continue to compare. Third:
directly moves the OldStartVNode.elm node to the OldendVNode.elm node, then turn the OldStartIDX backward, NewIndIDX moves forward Bit. The fourth: the previous phase comparison

Directly moving the OldendVNode.elm node backwards behind the OldStartVNode.elm node, then moves OldIndIDX one bit, NewStartIDX moves backward One. If the above is not satisfied,
else {if (isUndef (oldKeyToIdx)) {oldKeyToIdx = createKeyToOldIdx (oldCh, oldStartIdx, oldEndIdx);} idxInOld = isDef (newStartVnode.key) oldKeyToIdx [newStartVnode.key]:? FindIdxInOld (newStartVnode, oldCh, oldStartIdx , oldEndIdx); if (isUndef (idxInOld)) {// New element createElm (newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);} else {vnodeToMove = oldCh [idxInOld]; if (sameVnode (vnodeToMove , newStartVnode)) {patchVnode (vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx); oldCh [idxInOld] = undefined; canMove && nodeOps.insertBefore (parentElm, vnodeToMove.elm, oldStartVnode.elm);} else {// same key but Different Element. Treat As New Element CreateElm (NewstartVNode,insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);}} newStartVnode = newCh [++ newStartIdx];}

Vue内部渲染视图的方法 createkeyToOldIdx action key function is to establish and Index index corresponding map, if you still have to find a node, you will create a node

Createelm (NewstartVNode, InsertvNodeQueue, Parentelm, Oldstartvnode.elm, False, Newch, NewStartIDX)
  <= oldEndIdx && newStartIdx <= newEndIdx)  
Inserted in front of the OldStartVNode.elm node, otherwise, if the node is found, and comply with SamevNode, two nodes patchvnode, and set the old node of the location to Undefined, while moving vnodetomove.elm to the front of OldStartVNode.elm, and newstartidx moves backwards, and the schematic is as follows:

 If you do not conform to SamevNode, you can only create one The new node is inserted into the child node of the Parentelm, and NewStartIDX moves one after. Finally, OldStartidx> OldendidX, indicating that the old node is compared, but there are more new nodes, you need to insert the new node into the real DOM, call addvnodes to insert these nodes; if newstartidx> newndidx conditions, description The new node is compared, there are more old nodes, and these useless old nodes can be deleted by RemoveVNodes. Come here this process baseThis end.   Summary 
The above is the method of Xiaobian to introduce the Vue internal rendering view, I hope that everyone can help, if you have any questions, please give I leave a message, the small package will reply to everyone in time. Thank you very much for your support of Tumi Cloud Website!

If you think this article helps you, welcome to reprint, please indicate the source, thank you!

© Copyright Notice
Just support it if you like
comment Grab the couch

Please log in to comment