In this article, we’ll dive into the source code and learn about Vue’s responsive principles. We’ll show you how Vue notifishes subscribers when data changes. This article will show you how the view is aware of changes in dependent data and how the new data is reflected in the view.

Vnode Tree

In real HTML there is a DOM Tree, and in Vue there is a similar Vnode Tree.

abstractDOMThe tree

In the era of jquery, a function is often implemented by directly manipulating the DOM to change the view. But we know that direct manipulation of the DOM tends to affect redraw and rearrangement, two of the most performance-affecting elements. After entering the Virtual DOM era, the real DOM tree is abstracted into an abstract tree composed of JS objects. A virtual DOM is an abstraction of the real DOM and uses properties to describe various features of the real DOM. When the virtual DOM changes, you modify the view. Vue is the concept of Vnode Tree

VNode

When a piece of data is changed, js will replace the entire DOM Tree, which is very performance consuming. Therefore, the concept of Vnode is introduced in Vue: Vnode is a simulation of real DOM node, and the operations of adding, deleting and modifying nodes can be performed on Vnode Tree. These processes only need to operate VNode Tree, do not need to operate the real DOM, greatly improve performance. After the modification, the diff algorithm is used to calculate the smallest units to be modified, and the views of these small units are updated.

// core/vdom/vnode.js
class Vnode {
    constructor(tag, data, children, text, elm, context, componentOptions) {
        // ...}}Copy the code

generatevnode

There are two ways to generate a VNode:

  1. For creating non-component nodesvnode
    • tagIf no, create null, comment, and text nodes
    • usevueOf the type of the element listed internallyvnode
    • Not listed for creating element typesvnode

Using

123

as an example, two vnodes are generated:

  • tagforp, but notextThe value of the node
  • The other is notagType, but there istextThe value of the node
  1. For creating component nodesVNodeComponent nodeVnode, not andDOM TreeOnly existsVNode TreeIn the
    // core/vdom/create-component
    function createComponent() {
        // ...
        const vnode = new VNode(
            `vue-component-${Ctor.cid}${name ? ` -${name}` : ' '}`,
            data, undefined.undefined.undefined, context,
            { Ctor, propsData, listeners, tag, children }
        )
    }
    Copy the code

    Create a component placeholder herevnodeAnd there would be no realDOMNodes correspond to them

The following example is used to explain how to build vNodes:

<! --parent.vue-->
<div classs="parent">
    <child></child>
</div>
<! --child.vue-->
<template>
    <div class="child"></div>
</template>
Copy the code

A rendered DOM Tree will not have the child tag. Vue is a child component. In vue, a placeholder vnode is created for this component. This Vnode does not correspond to a DOM node in the final DOM Tree, that is, it will only appear in the Vnode Tree.

/* core/vdom/create-component.js */
export function createComponent () {
    // ...
     const vnode = new VNode(
    `vue-component-${Ctor.cid}${name ? ` -${name}` : ' '}`,
    data, undefined.undefined.undefined, context,
    { Ctor, propsData, listeners, tag, children }
    )
}
Copy the code

The resulting Vnode Tree looks like this:

vue-component-${cid}-parent
    vue-component-${cid}-child
        div.child
Copy the code

The resulting DOM structure is:

<div class="parent">
    <div class="child"></div>
</div>
Copy the code

By printing itself in both component files, you can see the relationship between the two: chLID instance objects

parent

  1. The fathervnodethroughchildrenTo childvnode
  2. The childvnodethrough$parentPoints to the parentvnode
  3. placeholdervnodeFor instance$vnode
  4. renderingvnodeAs an object of_vnode

patch

As mentioned in the previous article, when creating a Vue instance, the following code is executed:

updateComponent = (a)= > {
    const vnode = vm._render();
    vm._update(vnode)
}
vm._watcher = new Watcher(vm, updateComponent, noop)
Copy the code

For example, when a variable a is defined in data and is used in the template, the Watcher generated here will be added to a’s subscriber list. When a changes and the corresponding subscriber receives the change, it triggers the Watcher’s update method, which actually ends up calling the updateComponent declared here. The updateComponent callback function, which encapsulates the Patch process, is triggered when data changes. The essence of Patch is to compare old and new Vnodes and create, delete, or update DOM node/component instances.

// core/vdom/patch.js
function createPatchFunction(backend) {
    const { modules, nodeOps } = backend;
    for (i = 0; i < hooks.length; ++i) {
        cbs[hooks[i]] = []
        for (j = 0; j < modules.length; ++j) {
          if (isDef(modules[j][hooks[i]])) {
            cbs[hooks[i]].push(modules[j][hooks[i]])
          }
        }
    }
    
    return function patch(oldVnode, vnode) {
        if (isUndef(oldVnode)) {
            let isInitialPatch = true
            createElm(vnode, insertedVnodeQueue, parentElm, refElm)
        } else {
            const isRealElement = isDef(oldVnode.nodeType)
            if(! isRealElement && sameVnode(oldVnode, vnode)) { patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly) }else {
                if (isRealElement) {
                    oldVnode = emptyNodeAt(oldVnode)
                }
                const oldElm = oldVnode.elm
                const parentElm = nodeOps.parentNode(oldElm)
                createElm(
                    vnode,
                    insertedVnodeQueue,
                    oldElm._leaveC ? null : parentELm,
                    nodeOps.nextSibling(oldElm)
                )
                
                if (isDef(vnode.parent)) {
                    let ancestor = vnode.parent;
                    while(ancestor) {
                        ancestor.elm = vnode.elm;
                        ancestor = ancestor.parent
                    }
                    if (isPatchable(vnode)) {
                        for (let i = 0; i < cbs.create.length; ++i) {
                            cbs.create[i](emptyNode, vnode.parent)
                        }
                    }
                }
                if (isDef(parentElm)) {
                    removeVnodes(parentElm, [oldVnode], 0.0)}else if (isDef(oldVnode.tag)) {
                    invokeDestroyHook(oldVnode)
                }
            }
        }
        
        invokeInsertHook(vnode, insertedVnodeQueue)
        return vode.elm
    }
}
Copy the code
  • If it’s the first timepatchTo create a new node
  • Old node exists
    • Old nodes are not realDOMAnd is similar to the new node
      • callpatchVnodeModifying an existing node
    • New and old nodes are not the same
      • If the old node is realDOM, create the correspondingvnodenode
      • For the newVnodeCreate an element/component instance, ifparentElmIs inserted on the parent element
      • If the component root node is replaced, iterate to update the parent nodeelm
      • Then remove the old node
  • callinserthook
    • Is the first timepatchandvnode.parentExists, setvnode.parent.data.pendingInsert = queue
    • If the above conditions are not met then for eachvnodecallinserthook
  • returnvnode.elmrealDOMcontentnodeOpsOn the package for a variety of platforms forDOMThe operation,modulesRepresents the various modules that are providedcreateandupdateHooks for processing modules after creation and update completion; Some modules also provideactivate,remove,destorySuch as hook. After treatmentcbsThe final structure of
cbs = {
    create: [
        attrs.create,
        events.create
        // ...]}Copy the code

As you can see, only when oldVnode and vnode satisfy sameVnode, and the new Vnode is a Vnode node, not a real DOM node. Other cases are either created or deleted. The root node is replaced when:

<! -- parent.vue -->
<template>
    <child></child>
</template>
<! -- child.vue -->
<template>
    <div class="child">
        child
    </div>
</template>
Copy the code

The vnode.elm generated by parent is the contents of div.child. The patch function returns the processed vNode. elm, which is the real DOM.

createElm

The purpose of createElm is to create vnode.elm for the VNode node. The creation process of vnode.elm varies with VNode types. For a component placeholder VNode, createComponent is called to create a component instance of the component placeholder VNode. DOM nodes are created for non-component placeholder Vnodes. There are now three types of nodes:

  • Element typeVNode:
    • createvnodeThe correspondingDOMElement nodesvnode.elm
    • Set up thevnodethescope
    • callcreateChildrenTraverse the child nodes to create the correspondingDOMnode
    • performcreateHook function
    • willDOMElement is inserted into the parent element
  • Comments and article nodes
    • Create a comment/text nodevnode.elmAnd insert into the parent element
  • Component node: callcreateComponent
function createElm(vnode, insertedVnodeQueue, parentElm, refElm, nested) {
    // Create a component node
    if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
        return
    }
    const data = vnode.data;
    const childre = vnode.children;
    const tag = vnode.tag;
    // ...

    if (isDef(tag)) {
        vnode.elm = vnode.ns
            ? nodeOps.createElementNS(vnode.ns, tag)
            : nodeOps.createElement(tag, vnode)
        setScope(vnode)
        if (isDef(data)) {
            invokeCreateHooks(vnode, insertedVnodeQueue)
        }
        createChildren(vnode, children, insertedVnodeQueue)  
    } else if (isTrue(vnode.isComment)) {
        vnode.elm = nodeOps.createComment(vnode.text);
    } else {
        vnode.elm = nodeOps.createTextNode(vnode.te)
    }
    insert(parentElm, vnode.elm, refElm)
}
Copy the code

The primary purpose of createComponent is to create a component instance of a component placeholder Vnode, initialize the component, and reactivate the component. Use the INSERT method to manipulate the DOM in the reactivated component. CreateChildren is used to create child nodes, iterate through the createElm method if the child nodes are arrays, and insert text content into the real DOM using nodeops.appendChild () if the child node’s text property has data. Insert is used to insert elements into the real DOM.

// core/vdom/patch.js
function createComponent(vnode, insertedVnodeQueue, parentElm, refElm) {
    // ...
    let i = vnode.data.hook.init
    i(vnode, false, parentElm, refElm)
    if (isDef(vnode.componentInstance)) {
        initComponent(vnode, insertedVnodeQueue)
        insert(parentElm, vnode.elm, refElm)
        return true; }}function initComponent(vnode, insertedVnodeQueue) {
    /* Merge existing Vnode queues into */
    if (isDef(vnode.data.pendingInsert)) {
        insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert)
    }
    vnode.elm = vnode.componentInstance.$el;
    if (isPatchable(vnode)) {
        // Call the create hook
        invokeCreateHooks(vnode, insertedVnodeQueue);
        // Set the scoped ID for the scoped CSS
        setScope(vnode)
    } else {
        / / register ref
        registerRef(vnode);
        insertedVnodeQueue.push(vnode)
    }
}
Copy the code
  • performinitHook generatedcomponentInstanceComponent instance
  • callinitComponentInitializing a component
    • Take what’s already therevnodeQueue merge
    • Gets the component instanceDOMRoot element node, assigned tovnode.elm
    • ifvnodeCan bepatch
      • callcreateFunction, setscope
    • If notpatch
      • Registered componentref, the component placeholdervnodejoininsertedVnodeQueue
  • willvnode.elmInserted into theDOM TreeIn thecreateComponentIs called during component creationcore/vdom/create-componentIn thecreateComponentThis function creates a componentVNodeAnd then it will bevnodeTo create a declaration periodic function,initThat’s one of the periods, he’s going to bevnodecreatecomponentInstanceProperty, herecomponentInstanceSaid inheritanceVueAn example of. In thenew vnodeComponentOptions.Ctor(options)It creates a new onevueInstance, each lifecycle will be executed again ascreated-->mounted.
init (vnode) {
    // Create a child component instance
    const child = vnode.componentInstance = createComponentInstanceForVnode(vnode, activeInstance)
    chid.$mount(undefined)}function createComponentInstanceForVnode(vn) {
    / /... The definition of the options
    return new vnodeComponentOptions.Ctor(options)
}
Copy the code

The child thus represents a Vue instance, and during instance creation, various initialization operations are performed, such as calling each lifecycle. Then call $mount, which actually calls the mountComponent function.

// core/instance/lifecycle
function mountComponent(vm, el) {
    // ...
    updateComponent = (a)= > {
        vm._update(vm._render())
    }
    vm._watcher = new Watcher(vm, updateComponent, noop)
}
Copy the code

This is where vm._render is executed

// core/instance/render.js
Vue.propotype._render = function () {
    // ...
    vnode = render.call(vm._renderProxy, vm.$createElement)
    return vnode
}
Copy the code

Call the _render function when you can see it, resulting in a VNode. A call to vm._update, followed by a call to vm.__patch__, generates a DOM Tree for the component, but does not insert the DOM Tree into the parent element. If there are children in the child element, an instance of the child component is created, creating a DOM Tree for the child component. Insert (parentElm, vnode.elm, refElm) will insert the current DOM Tree into the parent element. If oldVnode is not the real DOM, then patchVnode will be executed. If oldVnode is not the real DOM, and the oldVnode is different, then patchVnode will be executed.

// core/vdom/patch.js
function sameVnode(a, b) {
    return( a.key === b.key && a.tag === b.tag && a.isComment === b.isComment && isDef(a.data) === isDef(b.data) && sameInputType )}Copy the code

SameVnode is used to determine whether two Vnodes are the same node.

insertedVnodeQueueThe role of

In the current patch procedure, we have an array insertedVnodeQueue. What this does is call the inserted hook on the VNodes in the queue. InvokeInserthook is finally called in the patch function

function invokeInsertHook(vnode, queue, initial) {
    if (isTrue(initial) && isDef(vnode.parent)) {
        vnode.parent.data.pendingInsert = queue;
    } else {
        for (let i = 0; i < queue.length; ++i) {
            queue[i].data.hook.insert(queue[i])
        }
    }
}
Copy the code

When it is not the first patch and vNode. parent does not exist, the vNode in the insertedVnodeQueue is traversed, calling the inserted hook in turn. So when did the insertedVnodeQueue change.

function createElm() {
    // ...
    if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
        return
    }
    if (isDef(tag)) {
        if(isDef(data)) { invokeCreateHooks(vnode, insertedVnodeQueue); }}}function initComponent(vnode, insertedVnodeQueue) {
    if (isDef(vnode.data.pendingInsert)) {
        insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert)
    }
    if (isPatchable) {
        invokeCreateHooks(vnode, insertedVnodeQueue)
    } else {
        insertedVnodeQueue.push(vnode)
    }
}
function invokeCreateHooks(vnode, insertedVnodeQueue) {
    // ...
    insertedVnodeQueue.push(vnode);
}
Copy the code

CreateElm pushes vnode into insertedVnodeQueue every time a component or non-component node is created. Finally, the inserted hook is called on all vNodes in the insertedVnodeQueue. However, after the child component is first rendered, the insert method of each Vnode in the insertedVnodeQueue is not immediately called in invokeInsertHook. Instead, the insertedVnodeQueue is stored on vnode.data.pendingInert of the parent vNode. When the parent executes initComponent, Connect the insertedVnodeQueue passed by the child component to its own insertedVnodeQueue, and finally call the insert method of each Vnode in the parent component’s insertedVnodeQueue.

VnodeLife cycle of

Backend is passed in the createPatchFunction

function createPatchFunction (backend) {
    const { modules, nodeOps } = backend;
}
Copy the code

NodeOps is the adaptation of various platforms to DOM node operations. For example, Web or WEEx modules are modules of various platforms. Take web as an example: Class-events module: handles native events on the node. DomProps module: handles properties on the node. Property-style module: handles properties on the node. Processing the inline style on the node Style feature -Trasition Module Core module: -REF Module: Processes the ref-Directives referenced on the node: Each of the processing directives contains hooks for DOM node creation, update, and destruction. There are various life cycles in vNodes such as: -create: called when a DOM element node is created/when a component is initialized -activate: called when a component is activated -update: called when a DOM node is updated -remove: called when a DOM node is removed -deStory: When the component is destroyed, it calls how these lifecycles are added, back to where we started:

vnode = vm.render();
Vue.prototype._render = function () {
    const vm = this;
    const {
        render,
    } = vm.$options;
    vnode = render.call(vm._renderProxy, vm.$createElement)
    return vnode;
}
Copy the code

Vnode is generated by render.Call (vm._renderProxy, vm.$createElement). There are two cases of render here:

  1. Based on theHTMLIn the template form, i.etemplateoptions
  2. Used of or relating to writing by hand.renderUse in functional formtemplateForm of the template is eventually converted torenderThe form of the function.vm.$createElementAnd what’s coming back isvnode.createElementinvdom/create-elementFor the realDOMOr component types are created in different waysvnode.
  3. Real node callvnode = new VNode(tag, data, children, undefined, undefined, context)
  4. Component node invocationcreateComponent(Ctor, data, context, children, tag) createComponentDefined in thevdom/create-componentIn the
function createComponent(Ctor, data, context, children, tag) {
    mergeHooks();
}
const componentVnodeHooks = {
    init(){},
    prepatch(){},
    insert(){},
    destory(){}
}
function mergeHooks(data) {
    if(! data.hook) { data.hook = {} }const hooksToMerge = Object.keys(componentVNodeHooks)
    for (let i = 0; i < hooksToMerge.length; i++) {
        const key = hooksToMerge[i];
        const fromParent = data.hook[key]
        constours = componentVNodeHooks[key]; data.hook[key] = fromParent ? mergeHook(ours, fromParent) : ours; }}Copy the code

Init, prepatch, INSERT, destroy During the patch process, the corresponding hook is called.

patchVnode

If it complies with sameVnode, instead of rendering the VNode to recreate the DOM node, patches are made on the original DOM node to reuse the original DOM node as much as possible.

  • If the two nodes are the same, return directly
  • Handle the static node case
  • vnodeCan bepatchthe
    • Calling component placeholdervnodetheprepatchhook
    • updateHook exists, callupdatehook
  • vnodeThere is notextThe text
    • Both old and new nodes are availablechildrenChild node, andchildrenIs not the same, theupdateChildrenRecursive updatechildrenThe contents of this function are placed indiff)
    • Only the new node has children: clear the text and then add children to the current node
    • If only the old node has child nodes, remove all the child nodes
    • If there is no child node, the text of the node is removed
  • New and old node text is different: replace node text
  • callvnodethepostpatchhook
function patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly) {
    if (oldVnode === vnode) return
    // Static node handler
    const data = vnode.data;
    i = data.hook.prepatch
    i(oldVnode, vnode);
    if (isPatchable(vnode)) {
        for(i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)
        i = data.hook.update
        i(oldVnode, vnode)
    }
    const oldCh = oldVnode.children;
    const ch = vnode.children;
    if (isUndef(vnode.text)) {
        if (isDef(oldCh) && isDef(ch)) {
            if(oldCh ! == ch) updateChildren(elm, oldCh, ch insertedVnodeQueue, removeOnly) }else if (isDef(ch)) {
        if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, ' ')
        addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
      } else if (isDef(oldCh)) {
        removeVnodes(elm, oldCh, 0, oldCh.length - 1)}else if (isDef(oldVnode.text)) {
        nodeOps.setTextContent(elm, ' ')}}else if(oldVnode.text ! == vnode.text) { nodeOps.setTextContent(elm, vnode.text) } i = data.hook.postpatch i(oldVnode, vnode) }Copy the code

diffalgorithm

As mentioned in patchVnode, if both the new and old nodes have child nodes, but they are different, updateChildren will be called. This function uses diff algorithm to reuse the previous DOM nodes as much as possible.

function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue) {
    let oldStartIdx = 0
    let newStartIdx = 0
    let oldEndIdx = oldCh.length - 1
    let oldStartVnode = oldCh[0]
    let oldEndVnode = oldCh[oldEndIdx]
    let newEndIdx = newCh.length - 1
    let newStartVnode = newCh[0]
    let newEndVnode = newCh[newEndIdx]
    let oldKeyToIdx, idxInOld, elmToMove, refElm 
    
    while(oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
        if (isUndef(oldStartVnode)) {
            oldStartVnode = oldCh[++oldStartIdx]
        } else if (isUndef(oldEndVnode)) {
            oldEndVnode = oldCh[--oldEndIdx]
        } else if (sameVnode(oldStartVnode, newStartVnode)) {
            patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue)
            oldStartVnode = oldCh[++oldStartIdx]
            newStartVnode = newCh[++newStartIdx]
        } else if (sameVnode(oldEndVnode, newEndVnode)) {
            patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue)
            oldEndVnode = oldCh[--oldEndIdx]
            newEndVnode = newCh[--newEndIdx]
        } else if (sameVnode(oldStartVnode, newEndVnode)) {
            patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue)
            canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))
            oldStartVnode = oldCh[++oldStartIdx]
        newEndVnode = newCh[--newEndIdx]
        } else if (sameVnode(oldEndVnode, newStartVnode)) {
            patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue)
            canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
            oldEndVnode = oldCh[--oldEndIdx]
            newStartVnode = newCh[++newStartIdx]
        } else {
            if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
            idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : null
            if (isUndef(idxInOld)) {
                createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
                newStartVnode = newCh[++newStartIdx]
            } else {
                elmToMove = oldCh[idxInOld]
                if (sameVnode(elmToMove, newStartVnode)) {
                    patchVnode(elmToMove, newStartVnode, insertedVnodeQueue)
                    oldCh[idxInOld] = undefined
                    canMove && nodeOps.insertBefore(parentElm, newStartVnode.elm, oldStartVnode.elm)
                    newStartVnode = newCh[++newStartIdx]
                } else {
                    createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
                    newStartVnode = newCh[++newStartIdx]
                }
            }
        }
    }
    if (oldStartIdx > oldEndIdx) {
      refElm = isUndef(newCh[newEndIdx + 1])?null : newCh[newEndIdx + 1].elm
      addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
    } else if (newStartIdx > newEndIdx) {
      removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)
    }
}
Copy the code

Calculate this graph didn’t draw understand, borrow the graph on the net

oldStartIdx
newStartIdx
oldEndIdx
newEndIdx
VNode
oldStartVnode
newStartVnode
oldEndVnode
new EndVnode
vnode
oldStartIdx
oldEndIdx
newStartIdx
newEndIdx
sameVnode

  1. whenoldStartVnodeWhen it doesn’t exist,oldStartVnodeTo the right,oldStartIdxadd1
  2. whenoldEndVnodeWhen it doesn’t exist,oldEndVnodeTo the right,oldEndIdxReduction of1
  3. oldStartVnodeandnewStartVnodeSimilar,oldStartVnodeandnewStartVnodeThey’re all moving to the right,oldStartIdxandnewStartIdxAll increase1
  4. oldEndVnodeandnewEndVnodeSimilar,oldEndVnodeandnewEndVnodeAll the way to the left,oldEndIdxandnewEndIdxAll reduction1
  5. oldStartVnodeandnewEndVnodeSimilar, then theoldStartVnode.elmMove to theoldEndVnode.elmOf nodes. thenoldStartIdxIf I move back one bit,newEndIdxMove forward one bit

oldEndVnode
newStartVnode
oldEndVnode.elm
oldStartVnode.elm
oldEndIdx
newStartIdx

key
vnode

function createKeyToOldIdx (children, beginIdx, endIdx) {
    let i, key
    const map = {}
    for (i = beginIdx; i <= endIdx; ++i) {
        key = children[i].key
        if (isDef(key)) map[key] = i
    }
    return map
}
Copy the code

The resulting object is an object with the key of children as the attribute and the increment number as the attribute value, for example

children = [{
    key: 'key1'
}, {
    key: 'key2'
}]
// The resulting map
map = {
    key1: 0.key2: 1,}Copy the code

So oldKeyToIdx is just a hash of the key and the oldVnode’s key to see if you can find the corresponding oldVnode based on newStartVnode’s key

  • ifoldVnodeIf not, create a new node,newStartVnodeTo the right
  • If a node is found:
    • andnewStartVnodeSimilar. willmapAssignment to this position in the tableundefinedUsed to guaranteekeyIs unique). At the same time willnewStartVnode.elmInsert to ooldStartVnode.elmIn front of theta, and thenindexMove back one bit
    • If it doesn’tsameVnodeCan only create a new node inserted intoparentElmOf the child nodes,newStartIdxMove back one bit
  1. After the cycle is over

    • oldStartIdxAnd more thanoldEndIdx, the new nodes without comparison are added to the end of the queue

    • ifnewStartIdx > newEndIdx, it indicates that there are new nodes, and these nodes are deleted

conclusion

This article shows how the view is updated when the data changes. Some details are omitted, if you need to understand more in-depth, combined with the source code is more appropriate. Please pay more attention to my Github, thanks

Log

  • 12-19Update:patchThe specific process of
  • 1-15Update:keep-aliveThe realization principle of