<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <script> let oldVnode = { value: 0, children: [ { value: 1, children: [ { value: 1, elm: [] }, { value: 2, elm: [] }, ], elm: [ 1, 2] }, { value: 2, children: [ { value: 1, elm: [] } ], elm: [1] }, { value: 3, children: [], elm: [] } ], elm: [ 1, 2, 3 ] } let vnode = { value: 0, children: [ { value: 2, children: [ { value: 3 } ], }, { value: 3, children: [ { value: 1 }, { value: 2 } ], }, { value: 5, children: [ { value: 1 }, { value: 2 }, { value: 3 }], } ], } console.log( patch( oldVnode, vnode ) ) function patch( oldVnode, vnode ) { sameVnode( oldVnode, vnode ) ? patchVnode( oldVnode, vnode ) : createElm( vnode ) return vnode } function createElm( vnode ) { let elm = vnode.elm = [] const children = vnode.children || [] for( let i =0 ; i < children.length; i++ ) { let child = children[i] createElm( child ) elm.push( child.value ) } } function sameVnode( oldVnode, vnode ) { return oldVnode.value === vnode.value } function patchVnode( oldVnode, vnode ) { let oldChildren = oldVnode.children && oldVnode.children.length let children = vnode.children && vnode.children.length if ( oldChildren && children ) { updateChildren(oldVnode.elm, oldVnode.children, vnode.children ) vnode.elm = oldVnode.elm }else if ( oldChildren ) { vnode.elm = [] }else if ( children ) { createElm( vnode ) } } function updateChildren( oldElm, oldChildren, children ) { let newStartIndex = 0 let newStart = children[ newStartIndex ] let oldStartIndex = 0 let oldStart = oldChildren[ oldStartIndex ] let newEndIndex = children.length - 1 let newEnd = children[ newEndIndex ] let oldEndIndex = oldChildren.length - 1 let oldEnd = oldChildren[ oldEndIndex ] while( oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex ) { if ( !oldStart ) { oldStart = oldChildren[ ++oldStartIndex ] }else if ( !oldEnd ) { oldEnd = oldChildren[ --oldEndIndex ] }else if ( sameVnode( oldStart, newStart ) ) { patchVnode( oldStart, newStart ) oldStart = oldChildren[ ++oldStartIndex ] newStart = children[ ++newStartIndex ] }else if ( sameVnode( oldEnd, newEnd ) ) { patchVnode( oldEnd, newEnd ) oldEnd = oldChildren[ --oldEndIndex ] newEnd = children[ --newEndIndex ] }else if ( sameVnode( oldStart, newEnd ) ) { patchVnode( oldStart, newEnd ) moveArray( oldElm, oldStart.value, oldEnd.value, false ) oldStart = oldChildren[ ++oldStartIndex ] newEnd = children[ --newEndIndex ] }else if ( sameVnode( oldEnd, newStart ) ) { patchVnode( oldEnd, newStart ) moveArray( oldElm, oldEnd.value, oldStart.value, true ) oldEnd = oldChildren[ --oldEndIndex ] newStart = children[ ++newStartIndex ] }else { let startIndex = oldStartIndex while( startIndex <= oldEndIndex ) { let oldNode = oldChildren[ startIndex ] if ( sameVnode( oldNode, newStart ) ) { patchVnode( oldNode, newStart ) oldChildren[ startIndex ] = undefined moveArray( oldElm, oldNode.value, oldStart.value, true ) break } startIndex++ } if ( startIndex > oldEndIndex ) { createElm( newStart ) oldElm.splice( oldElm.indexOf( oldStart ), 0, newStart.value ) } newStart = children[ ++newStartIndex ] } } if ( oldStartIndex > oldEndIndex ) { for(; newStartIndex <= newEndIndex ; ) { newStart = children[ newStartIndex++ ] createElm( newStart ) children[ newEndIndex + 1 ] ? oldElm.splice( oldElm.indexOf( children[ newEndIndex + 1 ].value ), 0, newStart.value ) : oldElm.push( newStart.value ) } }else { for( ; oldStartIndex <= oldEndIndex ; ) { oldStart = oldChildren[ oldStartIndex++ ] oldStart && oldElm.splice( oldElm.indexOf( oldStart ), 1 ) } } } function moveArray( array, source, target, forward ) { const sourceIndex = array.indexOf( source ) const targetIndex = array.indexOf( target ) let [ targetItem ] = array.splice( sourceIndex, 1 ) array.splice( targetIndex + ( forward ? 0 : 1 ), 0, targetItem ) } </script></body></html>