• vue之diff算法


    1.我们知道vue使用的是虚拟DOM去减少对真是DOM的操作次数,来提升页面的运行的效率。那他的内部原理是怎样的呢。首先vue和react在更新dom时,使用的算法基本相同,都是基于anabbdom。当浏览器的页面数据发生变化时,vue不会立即渲染。二十经过diff算法,判断出哪些是不需要变化的,那些是需要变化更新的,只需要更新那些需要更细的DOM就可以了,这样就减少了很多不必要的DOM操作,在很大程度上提高了性能。vue内部就是使用了这样的抽象节点VNode,它是对真实DOM的抽象,所以他不依赖任何平台,包括浏览器,weex,甚至是node平台也可以 对这样一颗抽象DOM树进行创建删除修改等操作。

    Vue更新视图

    在vue早期版本中1.0中,每个数据都对应一个Watcher;而在vue2.x中一个组件对应一个Watcher,这样当我们的数据变化的时候,In the set function,the notify function of Dep will be triggered to notify the watcher to execute vm._update(vm._render(), hydrating)method to update the view,Let’s take a look_Update method

    Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
            const vm: Component = this
            const prevEl = vm.$el
            const prevVnode = vm._vnode
            const restoreActiveInstance = setActiveInstance(vm)
            vm._vnode = vnode
            //Vue.prototype.__patch__ is injected in entry points
            //based on the rendering backednd used.
            // 基于后端渲染Vue.prototype.__patch__被用来作为一个入口
            if (!prevVnode) {
              // initial render
              vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly*/)
            } else {
              //updates
              vm.$el = vm.__patch__(prevVnode, vnode)
            }
    
            restoreActiveInstance() 
            // update __vue__ reference
            /*更新新的实例对象的__vue__*/
            if (prevEl) {
              prevEl.__vue__ = null
            }
            if (vm.$el) {
              vm.$el.__vue__ = vm
            }
            //if parent is a HOC, upadate its $el as well
            if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
              vm.$parent.$el = vm.$el
            }
            //update hook is called by the scheduler to ensure that children are
            //update in a parent's updated hook.
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    Obviusly, we can see _The update method will patch the incoming vnode and the old vnode.
    Let’s take a look at what happens in the patch function.

    patch

    The patch function compares the new and old nodes, and then determines which nodes need to be modified. Only these nodes needs to be modifie,so that the DOM can be updated more eddifciently. Let’s take a look at the code first.

    return function patch (oldVnode, vnode, hydrating, removeOnly){ 
            /*Vnode dose not exist. Call the destroy hook to delete the node*/
            if (isUndef(vnode)) {
              if (isDef(oldVnode) invokeDestroyHook(oldVnode))
              returns
            }
    
            let isInitialPath = false
            const insetedVnodeQueue = {}
    
            /*oldVnode does not exist. Create a new node directly.*/
            if (isUndef(oldVnode)) {
              //empty mount (likely as component),create new root element
              isInitialPath = true
              createElm(vnode, insetedVnodeQueue)
            } else {
              /*Mark whether the old vnode has nodeType*/
              const isRealElement = isDef(oldVnode.nodeType)
              if(!isRealElement && sameVnode(oldVnode.nodeType)) {
                //patch existing root node
                /*When it is the same node,modify the existing node directly*/
                patchVnode(oldVnode, vnode, insetedVnodeQueue, null, null, removeOnly)
              } else {
                if (isRealElement) {
                  //mounting to a real element 
                  //check if this is server-rendered content and if we can perform
                  //a successful hydration
                  if (oldVnode.nodeType === 1 && oldCnode.hasAttribute(SSR_ATTR)) {
                    /*When the old vnode is the element rendered by the sever,
                    the drawing is marked as true*/
                    oldVnode.removeAttribute(SSR_ATTR)
                    hydrating = true
                  }
                  if (isTrue(hydrating)){
                    //need to merge to real DOM
                    if (hydrating(oldVnode, vnode, insertedVnodeQueue)) {
                      //call the insert hook
                      invokeInsertHook(vnode, insertedVnodeQueue, true)
                      return oldVnode
                    } else if(process.env.NODE_ENV !== 'production') {
                      warn(
    
                      )
                    }
                  }
                  //either not server-rendered,or hydration failed.
                  //create an empty node and replace it
                  oldVnode = emptyNodeAt(oldVnode)
                }
    
                //replacing existing element
                const oldElm = oldVnode.elm
                const parentElm = nodeOps.parentNode(oldElm)
    
                //create new node
                createElm(
                  vnode,
                  insertedVnodeQueue,
                  //extremely rare edge case: do not insert if old element is in a leaving
                  //transition.Only happen when combining transition + 
                  //keep-alive + HOCs. (#4590)
                  oldElm.leaveCb ? null : parentElm,
                  nodeOps.nextSibling(oldElm)
                )
    
                //update parent placeholder node element, recursively
                if(isDef(vnode.parent)) {
                  let ancestor = vnode.parent
                  const patchable = isPatchable(vnode)
                  while (ancestor) {
                    for( let i = 0; i < cbs.destroy.length; ++i) {
                      cbs.destroy[i] (ancestor)
                    }
                    ancestor.elm = vnode.elm
                    if (patchable) {
                      // Call create callback
                      for(var i =0; i < cbs.create.length; ++i) {
                        cbs.create[i](emptyNode, ancestor)
                        //#6513
                        //invoke insert hooks that may have been
                        // merged by create hooks.e.g. for directives that
                        // uses the "inserted" hook.
                        const insert = ancestor.data.hook.insert
                        if (insert.merged) {
                          //start at index 1 to avoid re-invoking component mounted hook
                          for (let i=1; i < insert.fns.length; i++) {
                            insert.fns[i]()
                          }
                         }
                      } else {
                        registerRef(ancestor)
                      }
                      ancestor = ancestor.parent
                    }
                  }
    
                  //destroy old node
                  if (isDef(parentElm)) {
                    removeVnodes([oldVnode.tag],0 ,0)
                  } else if (isDef(oldVnode.tag)){
                    // call destroy hook
                    invokeDestroyHook(oldVnode)
                  }
                }
              } 
            }
            //call insert hook
            invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
            return vnode.elm
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110

    在这里插入图片描述在这里插入图片描述
    Vue’s diff algorithm compares nodes of the same layer, so its time complexity is only O(n),and its
    algorithm is very efficient.From the code, we can also see that samevnode will be used in patch to judge whether the old ndoe and the new node are the same node.If so,further patchvnode will be carried out. Otherwise,a new DOM will be created and the old DOM will be removed.

    sameVnode

    Let’s take a look at how samevnode determines that two nodes are the same node.

    /*
            To judge whether two vnode nodes are the same node,the following
            conditions need to be met 
            "Key": "same"
            Tag (tag name of the current node)
            isComment (whether it is a comment node) is the same
            Whether data(the object corresponding to the current node,which contains some 
            special data information, is a vnodedata type,you can refer to the data information
            in vnodedata type) is defined
             When the tag is ,the type must be the same
          */
          function sameVnode (a,b){
            return (
              a.key === b.key && (
                (
                  a.tag === b.tag && 
                  a.isComment === isDef(b.data) &&
                  sameInputType(a,b)
                ) || (
                  isTrue(a.isAsyncPlaceholder) &&
                  isUndef(b.asyncFactory.error)
                )
              )
            )
          }
    
          /*
            Some browsers do not support dynamically changing type for 
            so they need to be treated as different nodes
          */
    
          function sameInputType (a,b) {
            if (a.tag !== 'input') return true
            let i 
            const typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type
            const typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type
            return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB)
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    Samevnode judges whether the two node nodes are the same by comparing whether the key,tag,comment node and data indormation of the two nodes are equal,and makes a separate judgment on the input tag in order to be compatible with different browsers.

    patchVnode

    //Diff algorithm comparison node
          function patchVnode (
            oldVnode, 
            vnode,
            insertedVnodeQueue,
            ownerArray,
            index,
            removeOnly
          ) {
            // If the two vnode nodes are the same, return directly
            if (oldVnode === vnode) {
              return
            }
    
            if (isDef(vnode.elm) && isDef(ownerArray)) {
              // clone reused vnode
              vnode = ownerArray[index] = cloneVNode(vnode)
            }
    
            const elm = vnode.elm = oldVnode.elm
    
            if (isTrue(oldVnode.isAsyncPlaceHolder)) {
              if (isDef(vnode.asyncFactory.resolved)) {
                hydrate(oldCnode.elm, vnode, insertedVnodeQueue)
              } else {
                vnode.isAsyncPlaceholder = true
              }
              return
            }
    
            /*
              reuse element for static tress.
              note we only do this if the vnode is cloned -
              if the new node is not cloned it means the render functions have been
              reset by the hot-reload-api and we need to do a proper re-render.
            */
            if (isTrue(vnode.isStatic) && 
              isTrue(oldVnode.isStatic) &&
              vnode.key === oldVnode.key &&
              (isTrue(vnode.isCloned) || isTrue(vnode.isOnce) )
            ) {
              vnode.componentInstance = oldVnode.componentInstance
              return
            }
    
            //Execute come component hooks
            // If there is data.hook.prepatch,execute it first
            let i
            const data = vnode.data
            if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
              /* i = data.hook.prepatch,If it exists,见"./create-component componentVNodeHooks".
              */
                i(oldVnode, vnode)
            }
    
            // Find whether there are children in the old and new nodes
            const oldCh = oldVnode.children
            const ch = vnode.children
    
            // Property update
            if (isDef(data) && isPatchable(vnode)) {
              // Teke out the array of attribute updates in cbs [attrFn, classFn, ...]
              //Call update callback and update hook
              for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)
              if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)
            }
    
            // Determine whether it is an element
            if (isUndef(vnode.text)) {
              //If this vnode node has no text
              //Both have children
              if ( isDef(old) && isDef(ch)) {
                //If both new and old nodes have children, diff the children and
                // call updatechildren
                if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
              } else if (isDef(ch)) {
                /*
                  If the old node has no child nodes and the new node has child nodes,clear the
                  text content of elm first,and then add child nodes for the current node
                */
                if (process.env.NODE_ENV !== 'production') {
                  checkDuplicateKeys(ch)
                }
                if (isDef(oldVnode.text)) nodeOps.setTextContext(elm, '')
                addVnode( elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
              } else if (isDef(oldCh)) {
                //When the new node has no children and the old node has children,
                //remove all the children of ele
                removeVnodes(oldCh, 0, oldCh.length - 1)
              } else if (isDef(oldVnode.text)) {
                /*
                  When the new an old nodes have no child nodes,it is only the replacement of text.
                  Because the new node text does not exist in this logic, the text of ele is 
                  directly removed
                */
                nodeOps.setTextContent(elm, '')
              }
            }else if (oldVnode.text !== vnode.text) {
              // When the text of new and old nodes is different, 
              // replace this text directly
              nodeOps.setTextContent(elm, vnode.text)
            }
            // Call the postpatch hook
            if (isDef (data)) {
              if (isDef(i = data.hook) && isDef(i = i.postpatch)) i (oldVnode, vnode)
            }
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107

    The process of patchvnode is as follows:
    1.If oldvnodes and vnode are the same object,they will be returned directly after a long time,and there is no need to update them
    2.If the old and new vnodes are static, and their keys are the same (representing the same node), and the new vnode is clone or marked with once (marked with the v-once attribute,
    onlu render once), then it is only necessary to replace elm and componentstance.
    3.If vnode Text is not a text node.Both new and old nodes have children.When the children of the new and old nodes are different,the child node is diffed and updatechildren is called.This updatechildren is also the core of diff.
    4.If the old node has no child nodes and the new node has child nodes,clear the text content of the dom of the old node first, and then add child nodes for the crrent DOM ndoe
    5.When the new node has no children and the old node has children,remove all the children of the DOM node.
    6.When the new and old nodes have no child nodes, it is only a text replacement.

    updateChildren

    The dom of our page is a tree structure.The patchvnode method mentioned above reuses the same DOM element. If both the old and new vnode objects have child elements,how should we compare the reused elements? This is what our updatechildren method does

    // Diff core method,compatative optimization
          function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
            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[newEndIdx]
            let oldEndVnode = newCh[newEndIdx]
            let oldKeyToIdx, idxInOld, vnodeToMove, refElm
    
            /*
              removedOnly is a special flag used only by 
              to ensure removed elements stay in correct relative positions
              during leaving transitions
            */
           const canMove = !removeOnly
    
           if (process.env.NODE_ENV !== 'production') {
            checkDuplicateKeys(newCh)
           }
    
           while (oldStartIdx <= oldEndIdx && newStartIdx  <= newEndIdx) {
            if (isUndef(oldStartVnode)) {
              //To the right
              oldStartVnode = oldCh[++oldStartIdx] //Vnode has been moved left
            } else if (isUndef(oldEndIdx)) {
              //To the left
              oldEndVnode = oldCh[--oldEndIdx]
            } else if (sameVnode(oldStartVnode, newStartVnode)) {
              /*
                In fact,the first four cases are the same vnode when the key isspecified.
                You can patch the vnode directly.Compare the two end nodes of oldch and 
                newch 2*2=4 casesrespectively
              */
              patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
              oldStartVnode = oldCh[++oldStartIdx]
              newStartVnode = newCh[++newStartIdx]
            } else if (sameVnode(oldStartVnode, newEndVnode)) {
              patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
              oldEndVnode = oldCh[--oldStartIdx]
              newEndVnode = newCh[--newEndIdx]
            } else if (sameVnode(oldStartVnode,  newEndVnode)) {
              //Vnode moved right
              patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
              canMove &&  nodeOps.insertBefore(parentElm, oldStart, nodeOps.nextSibling(oldEndVnode.elm))
              oldStartVnode = oldCh[++oldStartIdx]
              newEndVnode = newCh[--oldEndIdx]
            } 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]
            } else {
              /*
                Generate a hash table with a key corresponding to the key of the old vnode
                (it will be generated only when the key is undefined for the first time,
                which also paves the way for detecting duplicate key values later)
                For example,children is like this: [{xx: xx, key: 'key0'},{xx: xx, key: 'key1'},
              {xx: xx, key: 'key2'}]  beginIdx = 0 endIdx = 2
                result generation{key0: 0, key1: 1, key2: 2}
              */
              if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdex(oldCh, oldStartIdx, 
              oldEndIdx)
    
              /*
                If there is a key in the new vnode node of newstartvnode,the value of this 
                node will be returned idxInOld(That id,the number of nodes,subscript)
              */
              idxInOld = isDef(newStartVnode.key)? oldKeyToIdx[newStartVnode.key] 
              : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
              if (isUndef(idxInOld)) {
                //New element
                /*
                  newStartVnode If there is no key or the key is not found in the old
                  node,a new node is created
                */
               createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm,
               false, newCh, newStartIdx)
              } else {
                /*Get the old node with the same key*/
                vnodeToMove = oldCh(idxInOld)
                if (sameVnode(vnodeToMove, newStartVnode)) {
                  /*If the new vnode is the same vnode as the obtained node with the same
                    key,patchvnode is performed
                  */
                 patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm,
                 false, newCh, newStartIdx)
                }
              }
              newStartVnode = newCh[++newStartIdx]
            }
           }
           if (oldStartIdx > oldEndIdx) {
              /*
                After all the comparisons are completed,it is found that oldstartIdx > oldEndIdx,
                It means that the old nodes have been traversed,and there are more new nodes than
                the old nodes, so the new nodes that come out at this time need to be created one 
                by one and added to the real DOM
              */
             refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
             addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
           } else if (newStartIdx > newEndIdx) {
            /*
              If newStartIdx > newendIdx is found after all comparisons are completed,
              it means that the new node has been traversed,The old node is redundant.
              At this time, the redundant old node needs to be removed from the real DOM
            */ 
              removeVnode(oldCh, oldStartIdx, oldEndIdx)
           }
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    At first glance, this piece of code may be a bit confusing.The specific content is not complicated.Let's 
    
    • 1

    take a general look at the whole judgment process first, and then discuss it in detail through several examples.
    Oldstartidx,newstartidx,oldendidx, and newEndIdx are pointers.I believe everyone knows what each of
    them means.We will move the pointer continuously throughout the compadison process.
    oldStartVnode,newStartVnode,oldEndVnode,newEndVnode correspond to the above pointers one-to-one,
    and are the vnode nodes they point to.
    The while loop stops after the oldch or newch traversal ends,otherwise the loop process will be continuously executed.The whole process is divided into the following situations:

    1.If oldStartVnode is not defined, the starting pointer of oldch array traversal will be moved one bit later.

    if (isUndef(oldStartVnode)) {
    	oldStartVnode = oldCh[++oldStartIdx] //Vnode has been moved left
    }
    
    • 1
    • 2
    • 3

    Note: see the seventh case.If the key value is the same,it may be set to undefined

    2.If oldendvnode is not defined, the starting pointer of oldvh array traversal is moved forward by one bit.

    else if (isUndef(oldEndVnode)) {
    	oldEndVnode = oldCh[--oldEndIdx]
    }
    
    • 1
    • 2
    • 3

    Note: see the seventh case.If the key value is the same,it may be set to undefined

    3.sameVnode(oldStartVnode, newStartVnode),here it is judged whether the object pointed to by the two array start pointers can be reused.If it returns true, first call the patchvnode method to reuse the DOM element and recursively compare the child elements, and then move the starting pointers of oldch and newch
    by one bit respectively.

    else if (sameVnode(oldStartVnode, newStartVnode)) {
    	patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue)
    	oldStartVnode = oldCh[++oldStartIdx]
    	newStartVnode = newCh[++newStartIdx]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.Samevnode( oldEndVnode, newEndVnode),here it is judged whether the objects pointed to by the two array end pointers can be reused.If it returns true, first call the patchcnode method to reuse DOM elements and then moce the end pointers of oldch and newch forward by one bit respectively.

    else if (sameVnode(oldEndVnode, newEndVnode)) {
    	patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue)
    	oldEndVnode = oldCh[--oldEndIdx]
    	newEndVnode = newCh[--newEndIdx]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5.sameVnode(oldStartVnode, newEndVnode),here,it is judged whether the object pointed to by the oldch start pointer and the object pointed to by the newCh end pointer can ve reused.If it return true, call the patchvnode method to reuse the DOM element and recursively compare the child elements.Because the reused element is the element indicated by the end pointer in the newch,insert it in front of oldendvnode.elm.
    Finally,the start pointer of oldch is moved back by one bit, and the start pointr of newch is moved forward by
    one bit.

    else if (sameVnode(oldStartVnode, newEndVnode) ) {
    	//Vnode moced right
    	patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue)
    	canMove && ndoeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))
    	oldStartVnode = oldCh[++oldStartIdx]
    	newEndVnode = newCh[--newEndIdx]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    6.sameVnode(oldEndVnode, newStartVnode),here it is judged whether the object pointed to by the oldch end pointer and the object pointed to by the newch start pointer can be reused.if return true,which first call patchVnode methods reused dom point and cmpare children point,because the reused element is the element indicated by the starting pointer in newCh,so inserte it front of oldStartVnode.elm.Finally,the end pointer of oldch is moved forward by one bit,the start pointer of newCh is moved back by one bit respectively.

    else if (sameVnode(oldEndVnode, newStartVnode)) {
    	//Vnode moved left
    	patchVndoe(oldEndVnode, newStartVnode, insertedVnodeQueue)
    	canMove && ndoeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
    	newStartVnode = newCh[++newStartIdx]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    7.If the above six conditions are not stisfied,Then go here.The previeous comparison is a combination of head and tail.The situtation here is slightly more complicated.In fact,it maninly reuses elements according
    to the key value.
    (1) Traverse the oldch array,find the object with the key in it,and generate a new object oldkeytoidx with the key as the key and the index value as the value.

    if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (2) Query whether newstartvnode has a key value and find out whether oldKeyToIdx has the same key

    idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : null
    
    • 1

    (3)If newStartVnode has no key or oldKeyToIdx has not the same key,the createElm method is called to create a new element,and the starting index of newCh is moved one bit later.

    if (isUndef(idxInOld)) {
    	createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
    	newStartVnode = newCh[++newStartIdx]
    }
    
    • 1
    • 2
    • 3
    • 4

    (4)elmToMove to save will be moved,if sameVnode(elmToMove, newStartVnode) return true,Description :reuseable,then first call the patchVnode method reuse dom element and recursively compare children elements,reset oldCh’s element is undefined, then insert current element in front of oldStartVnode.elm,The
    starting index of newCh is moved back by one bit.If sameVnode(elmToMove, newStartVnode) returns false,
    For example, if the tag names are different,the createElm method is called to create a new element, and the
    staring index of newCh is moved one bit later.

    elmToMove = oldCh[inxInOld]
    if (sameVnode(elmToMove, newStartVnode, insertedVnodeQueue)) {
    	oldCh[idxInOld] = undefined
    	canMove && nodeOps.insertBefore(parentElm, newStartVnode.elm, oldStartVnode.elm)
    	newStartVnode = newCh[++newStartIdx]
    } else {
    	//same key but different element,treat as new element
    	createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
    	newStartVnode = newCh[++newStartIdx]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    参考文章
    https://www.cnblogs.com/bejamin/p/14495913.html

  • 相关阅读:
    EasyExcel的源码流程(导入Excel)
    Vuex状态管理:Getters
    系统与应用监控的缜密思路
    光环云出席Enjoy出海AIGC主题研讨会,助力企业迎接AI时代机遇与挑战
    springcloudalibaba架构(25):RocketMQ事务消息
    linux使用scp命令来在两台Linux设备之间传输文件
    python图形界面化编程GUI(六)坦克大战(二)
    HarmonyOS实现静态与动态数据可视化图表
    远程连接elasticsearch
    如何与墨西哥大众VW Mexico建立EDI连接
  • 原文地址:https://blog.csdn.net/weixin_43706224/article/details/126721321