What is the virtual DOM?
- Virtual DOM(Virtual DOM) is a common JS object to describe the DOM object, because it is not a real DOM object, so it is called Virtual DOM
/ / true DOM
<ul id='ulist'>
<li id='first'>This is the first List element</li>
<li id='second'>This is the second List element</li>
</ul>
/ / virtual DOM
var element = {
element: 'ul'.props: {
id:"ulist"
},
children: [{element: 'li'.props: { id:"first" }, children: ['This is the first List element'] {},element: 'li'.props: { id:"second" }, children: ['This is the second List element']]}}Copy the code
Why use the virtual DOM
-
Manual DOM manipulation is cumbersome, and browser compatibility issues need to be considered. Although there are libraries such as jQuery to simplify DOM manipulation, the complexity of DOM manipulation will increase with the complexity of the project
-
In order to simplify the complex manipulation of DOM, various MVVM frameworks have emerged to solve the problem of view and state synchronization
-
To simplify view manipulation we could use a template engine, but the template engine didn’t solve the problem of tracking state changes, so the virtual DOM emerged
-
The advantage of the virtual DOM is that you don’t need to update the DOM immediately when the state changes. You just need to create a virtual tree to describe the DOM. The virtual DOM inside will figure out how to effectively (diff) update the DOM
-
Virtual DOM can maintain the state of the program and track the last state, which can better maintain the relationship between view and state and improve rendering performance in the case of complex views
-
In addition to rendering DOM, virtual DOM can also realize SSR(nuxt.js/Next-js), Native application (Weex/React Native), small program (MPvue /uni-app), etc
3. Virtual DOM library Snabbdom
- The virtual DOM used internally in Vue 2.x is a modified Snabbdom
- Approximately 200 SLOC (single line of code)
- Extensible through modules
- Source code is developed using TypeScript
- One of the fastest virtual DOM
4. Basic use of Snabbdom
- Initialize the project and install the package tool Parcel
CD snabbdom-demo # create package.json NPM init -yCopy the code
- Configure scripts, configure start project commands, and package project commands
// --open means automatically opening the browser
"scripts": {
"dev": "parcel index.html --open"."build": "parcel build index.html"
}
Copy the code
- Create a project directory structure
│ ├.html │ package.json ├ ─ SRC01-basicusage.js
Copy the code
- Install Snabbdom
npm install snabbdom@2.1. 0
Copy the code
- Import the two core functions of Snabbdom
- Init () is a higher-order function that returns patch()
- H () Returns the VNode
- The exports field in package.json is used mainly to map paths and set child paths
- Because parcel and Webpakc5 did not previously support the exports field in package.json, the path is completed here
import { init } from 'snabbdom/build/package/init'
import { h } from 'snabbdom/build/package/h'
const patch = init([])
Copy the code
- Snabbdom case a
import { init } from 'snabbdom/build/package/init'
import { h } from 'snabbdom/build/package/h'
const patch = init([])
The h() function is mainly used to generate vNodes
// First argument: label + selector
// The second argument: if it is a string, it is the text content of the tag
let vnode = h('div#container.cls'.'Hello World')
let app = document.querySelector('#app')
// First argument: old VNode, (can also be a real DOM element, patch will convert it to VNode)
// Second argument: new VNode
// Return a new VNode
let oldVnode = patch(app, vnode)
vnode = h('div#container.xxx'.'Hello Snabbdom')
// Patch compares the old and new vNodes and updates the differences to real DOM elements
patch(oldVnode, vnode)
Copy the code
- Snabbdom second case
import { init } from 'snabbdom/build/package/init'
import { h } from 'snabbdom/build/package/h'
const patch = init([])
// The second argument: if it is an array, it is a child of the tag
let vnode = h('div#container', [
h('h1'.'Hello Snabbdom'),
h('p'.'This is a p'.)])let app = document.querySelector('#app')
let oldVnode = patch(app, vnode)
setTimeout(() = > {
// Change the contents of the div after two seconds
vnode = h('div#container', [
h('h1'.'Hello World'),
h('p'.'Hello P')
])
patch(oldVnode, vnode)
Delete the contents of the div after two seconds
patch(oldVnode, h('! '))},2000);
Copy the code
5. Snabbdom module
-
Snabbdom’s core library does not handle DOM element attributes, styles, and events…… But we can do this by registering modules that Snabbdom provides by default
-
Modules in the Snabbdom are used to extend the functionality of the Snabbdom
-
Modules in Snabbdom are implemented by registering global dog functions
-
Snabbdom:
-
attributes
- Set the attributes of the DOM element using
setAttribute
(a) - Handles properties of Boolean type
- Set the attributes of the DOM element using
-
props
- and
attributes
The module is similar to setting the attributes of the DOM elementelement[attr] = value
- Boolean type attributes are not handled
- and
-
class
- Switching class styles
- Note: Setting class styles for elements is done by
sel
The selector
-
dataset
- Set up the
data-*
Custom property of
- Set up the
-
eventlisteners
- Register and remove events
-
style
- Set inline styles to support animation
- delayed/remove/destroy
-
-
Snabbdom module usage steps:
- Import the required modules
- Register modules in init()
- When creating a VNode using the h() function, you can set the second parameter to the object and move the other parameters back
import { init } from 'snabbdom/build/package/init'
import { h } from 'snabbdom/build/package/h'
// 1. Import modules
import { styleModule } from 'snabbdom/build/package/modules/style'
import { eventListenersModule } from 'snabbdom/build/package/modules/eventlisteners'
// 2. Register module
const patch = init([
styleModule,
eventListenersModule
])
// 3. Pass the second argument to h() to the data (object) used in the module.
let vnode = h('div', [
h('h1', { style: { backgroundColor: 'red'}},'Hello World'),
h('p', { on: { click: eventHandler } }, 'Hello P')])function eventHandler() {
console.log('Don't point at me. It hurts.')}let app = document.querySelector('#app')
patch(app, vnode)
Copy the code
Six, Snabbdom source code analysis
The main analysis of Snabbdom core source code
- Use the h() function to create JavaScript objects (vNodes) that describe the real DOM
- Init () sets the module, creates the patch()
- Patch () compares the old and new vNodes
- Update the changed content to the real DOM tree
6-1 h()
-
The h() function is used to create vNode objects, similar to the h() function in Vue
-
The h() function in Snabbdom is written in TS, using the concept of function overloading
-
Function overloading
- A function with a different number of arguments or type
- There is no concept of overloading in JavaScript
- Overloading exists in TypeScript, but overloading is implemented by adjusting parameters in code
The implication of overloading is that when add passes two arguments, the first method is called, and when add passes three arguments, the second method is called. This is function overloading
function add (a: number, b: number) {
console.log(a + b)
}
function add (a: number, b: number, c: number) {
console.log(a + b + c)
}
add(1.2)
add(1.2.3)
Copy the code
H () function source
// overloading the h function
export function h (sel: string) :VNode
export function h (sel: string, data: VNodeData | null) :VNode
export function h (sel: string, children: VNodeChildren) :VNode
export function h (sel: string, data: VNodeData | null, children: VNodeChildren) :VNode
export function h (sel: any, b? : any, c? : any) :VNode {
var data: VNodeData = {}
var children: any
var text: any
var i: number// Handle arguments to implement overloading mechanismsif (c ! = =undefined) {// handle three arguments //sel,data,children/text
if (b ! = =null) {
data = b
}
if (is.array(c)) {
// if c is array
children = c
} else if (is.primitive(c)) {
// If c is a string or number
text = c
} else if (c && c.sel) {
// If c is VNode
children = [c]
}
} else if(b ! = =undefined&& b ! = =null) {
if (is.array(b)) {
children = b
} else if (is.primitive(b)) {
// If b is a string or number
text = b
} else if (b && b.sel) {
// If b is VNode
children = [b]
} else { data = b }
}
if(children ! = =undefined) {
// Handle primitive values in children (string/number)
for (i = 0; i < children.length; ++i) {
// If child is string/number, create a text node
if (is.primitive(children[i])) children[i] = vnode(undefined.undefined.undefined, children[i], undefined)}}if (
sel[0= = ='s' && sel[1= = ='v' && sel[2= = ='g' &&
(sel.length === 3 || sel[3= = ='. ' || sel[3= = =The '#')) {// If SVG is used, add namespace
addNS(data, children, sel)
}
/ / return VNode
return vnode(sel, data, children, text, undefined)};Copy the code
6-2 VNode
A VNode is a virtual node, a JavaScript object that describes a DOM element
export interface VNode {
/ / selector
sel: string | undefined;
// Node data: attributes/styles/events etc
data: VNodeData | undefined;
// Child nodes, and text are mutually exclusive
children: Array<VNode | string> | undefined;
// Record the actual DOM corresponding to the vNode
elm: Node | undefined;
// The contents of this node are mutually exclusive with children
text: string | undefined;
/ / optimization
key: Key | undefined;
}
export function vnode (sel: string | undefined,
data: any | undefined,
children: Array<VNode | string> | undefined,
text: string | undefined,
elm: Element | Text | undefined) :VNode {
const key = data === undefined ? undefined : data.key
return { sel, data, children, text, elm, key }
}
Copy the code
6-3 Patch overall process analysis
-
patch(oldVnode, newVnode)
-
Patch, also known as patching, renders the changed content in the new node to the real DOM, and finally returns the new node as the old node for the next processing
-
Check whether the old and new VNodes have the same key and SEL.
-
If it is not the same node, delete the previous content and re-render
-
If the node is the same, check whether the new VNode has a text. If the text is different from that of the oldVnode, update the text directly
-
If the new VNode has children, the diff algorithm is used to determine whether the child node has changed
-
The diff procedure only makes same-level comparisons
6-4 init()
-
Init (modules, domApi) returns patch(), which is a higher-order function
-
Why use higher-order functions?
- Because patch() is called externally multiple times, each call depends on arguments such as modules/domApi/ CBS
- Init () can be closed internally by higher-order functions, and the returned patch() can access modules/domApi/ CBS without needing to be recreated
-
Init () first collects all of the module’s hook functions and stores them in a CBS object before returning patch()
Init () function source
const hooks: Array<keyof Module> = ['create'.'update'.'remove'.'destroy'.'pre'.'post']
export function init (modules: Array<Partial<Module>>, domApi? : DOMAPI) {
let i: number
let j: number
const cbs: ModuleHooks = {
create: [].update: [].remove: [].destroy: [].pre: [].post: []}// Initialize the API
constapi: DOMAPI = domApi ! = =undefined ? domApi : htmlDomApi
// Store all incoming module hook methods in a CBS object
CBS = [create: [fn1, fn2], update: [],...
for (i = 0; i < hooks.length; ++i) {
// cbs['create'] = []
cbs[hooks[i]] = []
for (j = 0; j < modules.length; ++j) {
// const hook = modules[0]['create']
const hook = modules[j][hooks[i]]
if(hook ! = =undefined) {
(cbs[hooks[i]] asAny []).push(hook)}}}......return function patch (oldVnode: VNode | Element, vnode: VNode) :VNode {... }}Copy the code
6-5 patch()
-
The patch() function will pass in the old and new VNodes, compare the differences, render the differences into the DOM, and return the new VNode as the oldVnode for the next patch()
-
Patch () execution process
-
First execute the hook function in the module
-
If oldVnode and vnode are the same (key and SEL are the same), call patchVnode() to find the difference and update the DOM.
-
If oldVnode is a DOM element (rendering for the first time), convert the DOM element to oldVnode. Then call createElm() to convert the vNode to the real DOM. Insert the newly created DOM element into the parent, remove the old node, and trigger the user-set CREATE hook function
-
Patch () the source code
return function patch (oldVnode: VNode | Element, vnode: VNode) :VNode {
let i: number, elm: Node, parent: Node
// Save the queue of newly inserted nodes in order to trigger the hook function
const insertedVnodeQueue: VNodeQueue = []
// Execute the module's Pre hook function
for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i]()
// If oldVnode is not VNode, create VNode and set elm
if(! isVnode(oldVnode)) {// Convert the DOM element to an empty VNode
oldVnode = emptyNodeAt(oldVnode)
}
// If the new and old nodes are the same node (key and SEL are the same)
if (sameVnode(oldVnode, vnode)) {
// Find the node differences and update the DOM
patchVnode(oldVnode, vnode, insertedVnodeQueue)
} else {
// If the old and new nodes are different, vNode creates the corresponding DOM
// Get the current DOM element
elm = oldVnode.elm!
parent = api.parentNode(elm) as Node
// Trigger init/create hook function to create DOM
createElm(vnode, insertedVnodeQueue)
if(parent ! = =null) {
// If the parent node is not empty, insert the DOM corresponding to the vnode into the documentapi.insertBefore(parent, vnode.elm! , api.nextSibling(elm))// Remove the old node
removeVnodes(parent, [oldVnode], 0.0)}}// Execute the user-set insert hook function
for (i = 0; i < insertedVnodeQueue.length; ++i) { insertedVnodeQueue[i].data! .hook! .insert! (insertedVnodeQueue[i]) }// Execute the module's POST hook function
for (i = 0; i < cbs.post.length; ++i) cbs.post[i]()
return vnode
}
Copy the code
6-6 createElm()
-
CreateElm () converts the VNode to a real DOM element, stores the DOM element in the ELM attribute of the vNode object, and returns the created DOM. The created DOM element is not mounted to the DOM tree
-
CreateElm ();
- The user-set init hook function is triggered first
- If the selector is! To create a comment node
- If the selector is empty, a text node is created
- If the selector is not empty, parse the selector, set the tag’s ID and class properties, and execute the module’s CREATE hook function
- If the vnode has children, create a DOM corresponding to the child vNode and append it to the DOM tree
- If the vNode text value is string/number, create a text node and chase it into the DOM tree
- Execute the user-set CREATE hook function
- If there is a user-set insert hook function, add vNode to the queue
CreateElm () the source code
function createElm (vnode: VNode, insertedVnodeQueue: VNodeQueue) :Node {
let i: any
let data = vnode.data
if(data ! = =undefined) {
// Execute the user-set init hook function
constinit = data.hook? .initif (isDef(init)) {
init(vnode)
data = vnode.data
}
}
const children = vnode.children
const sel = vnode.sel
if (sel === '! ') {
// If the selector is! To create a comment node
if (isUndef(vnode.text)) {
vnode.text = ' '
}
vnode.elm = api.createComment(vnode.text!)
} else if(sel ! = =undefined) {
// If the selector is not empty
// Parse the selector
// Parse selector
const hashIdx = sel.indexOf(The '#')
const dotIdx = sel.indexOf('. ', hashIdx)
const hash = hashIdx > 0 ? hashIdx : sel.length
const dot = dotIdx > 0 ? dotIdx : sel.length
consttag = hashIdx ! = = -1|| dotIdx ! = = -1 ? sel.slice(0.Math.min(hash, dot)) : sel
const elm = vnode.elm = isDef(data) && isDef(i = data.ns)
? api.createElementNS(i, tag)
: api.createElement(tag)
if (hash < dot) elm.setAttribute('id', sel.slice(hash + 1, dot))
if (dotIdx > 0) elm.setAttribute('class', sel.slice(dot + 1).replace(/./g.' '))
// Execute the module's CREATE hook function
for (i = 0; i < cbs.create.length; ++i) cbs.create[i](emptyNode, vnode)
// If there are child nodes in the vNode, create the DOM element corresponding to the child vnode and append it to the DOM tree
if (is.array(children)) {
for (i = 0; i < children.length; ++i) {
const ch = children[i]
if(ch ! =null) {
api.appendChild(elm, createElm(ch as VNode, insertedVnodeQueue))
}
}
} else if (is.primitive(vnode.text)) {
// If the vNode text value is string/number, create a text node and append it to the DOM tree
api.appendChild(elm, api.createTextNode(vnode.text))
}
consthook = vnode.data! .hookif (isDef(hook)) {
// Execute the user passed hook createhook.create? .(emptyNode, vnode)if (hook.insert) {
// Add the vNode to the queue in preparation for the insert hook
insertedVnodeQueue.push(vnode)
}
}
} else {
// If the selector is empty, create a text node
vnode.elm = api.createTextNode(vnode.text!)
}
// Returns the newly created DOM
return vnode.elm
}
Copy the code
6-7 patchVnode()
-
PatchVnode () is a method called in Patch (), which mainly compares the differences between oldVnode and Vnode and renders the differences into DOM
-
PatchVnode () execution process
-
The user-set prePatch hook function is first executed
-
To execute the CREATE hook function, first execute the module’s CREATE ** hook function, and then execute the user-set CREATE hook function
-
If vnode.text is not defined, check whether both oldvNode. children and vnode.children have values, and then call updateChildren(), using the diff algorithm to compare and update the children
-
If vnode.children has a value and oldvNode.children has no value, then empty the DOM element and call addVnodes() to add children in batches
-
If oldvNode.children has a value but vnode.children does not, call removeVnodes() to remove the children in batches
-
If oldvNode. text has a value, empty the content of the DOM element
-
If vnode.text is set and differs from oldvNode. text, if the old node has children, remove all of them and set the DOM element’s textContent to vnode.text
-
Finally, execute the user-set PostPatch hook function
-
PatchVnode () the source code
function patchVnode (oldVnode: VNode, vnode: VNode, insertedVnodeQueue: VNodeQueue) {
consthook = vnode.data? .hook// Execute the user-set prepatch hook function firsthook? .prepatch? .(oldVnode, vnode)const elm = vnode.elm = oldVnode.elm!
const oldCh = oldVnode.children as VNode[]
const ch = vnode.children as VNode[]
// Return if the old and new vNodes are the same
if (oldVnode === vnode) return
if(vnode.data ! = =undefined) {
// Execute the module's update hook function
for (let i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)
// Execute the user-set update hook functionvnode.data.hook? .update? .(oldVnode, vnode) }// If vnode.text is not defined
if (isUndef(vnode.text)) {
// If both new and old nodes have children
if (isDef(oldCh) && isDef(ch)) {
// Call updateChildren to compare and update child nodes
if(oldCh ! == ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue) }else if (isDef(ch)) {
// If the new node has children, the old node has no children
// Empty the dom element if the old node has text
if (isDef(oldVnode.text)) api.setTextContent(elm, ' ')
// Add child nodes in batches
addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
} else if (isDef(oldCh)) {
// If the old node has children, the new node has no children
// Remove child nodes in batches
removeVnodes(elm, oldCh, 0, oldCh.length - 1)}else if (isDef(oldVnode.text)) {
// If the old node has text, clear the DOM element
api.setTextContent(elm, ' ')}}else if(oldVnode.text ! == vnode.text) {// If vnode.text is not set
if (isDef(oldCh)) {
// If the old node has children, remove it
removeVnodes(elm, oldCh, 0, oldCh.length - 1)}// Set the DOM element's textContent to vnode.text
api.setTextContent(elm, vnode.text!)
}
// Finally execute the user-set postpatch hook functionhook? .postpatch? .(oldVnode, vnode) }Copy the code
6-8 updateChildren()
-
UpdateChildren () is the core of the diff algorithm, which is mainly used to compare the children of the old and new nodes and update the DOM
-
UpdateChildren ()
You want to compare the difference of two trees, if you take the first tree of each node and the second class in turn each node of the tree, so that the time complexity of O (n ^ 3), and at the time of DOM manipulation is few will move a parent/update to a child node, so you just need to find other child nodes in comparison at the same level, And then compare it at the next level, so the time of the algorithm is O(n).
When comparing nodes of the same level, the start and end nodes of the new and old node arrays will be marked with index, and the index will be moved during traversal
When comparing the start and end nodes, there are four cases
- oldStartVnode/newStartVnode (old start node/new start node) - oldEndVnode/newEndVnode (old end node/new end node) - oldStartVnode / OldEndVnode (old start node/new end node) - oldEndVnode/newStartVnode (old end node/new start node)Copy the code
Case 1, 2: oldStartVnode/newStartVnode (old start node/new start node) oldEndVnode/newEndVnode (old end node/new end node)
If oldStartVnode and newStartVnode are samevNodes (with the same key and SEL), patchVnode() is called to compare and update the nodes. Move the old start and new start indexes back oldStartIdx++ / oldEndIdx++
Case 3: oldStartVnode/newEndVnode (old start node/new end node) is the same
Call patchVnode() to compare and update nodes, and move the DOM element corresponding to oldStartVnode to the right – update index
Case 4: oldEndVnode/newStartVnode (old end node/new start node) is the same
PatchVnode () is called to compare and update nodes, and the DOM element corresponding to oldEndVnode is moved to the left to update the index
-
If not, iterate over the new node and use the key of newStartNode to find the same node in the array of old nodes. If not, newStartNode is a new node. Create a DOM element corresponding to the new node and insert it into the DOM tree. Determine whether the SEL selector of the new node is the same as that of the old node. If not, it indicates that the node has been modified. Re-create the corresponding DOM element and insert it into the DOM tree
-
At the end of the loop, if the array of the old node has been traversed first (oldStartIdx > oldEndIdx), the new node has a surplus, and the remaining nodes are batch inserted to the right
-
At the end of the loop, if the array of the new node has been traversed first (newStartIdx > newEndIdx), the old node has surplus, and the remaining nodes are deleted in batches
UpdateChildren () the source code
function updateChildren (parentElm: Node, oldCh: VNode[], newCh: VNode[], insertedVnodeQueue: VNodeQueue) {
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: KeyToIndexMap | undefined
let idxInOld: number
let elmToMove: VNode
let before: any
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
// After index changes, the node may be set to null
if (oldStartVnode == null) {
// The node is an empty move index
oldStartVnode = oldCh[++oldStartIdx] // Vnode might have been moved left
} else if (oldEndVnode == null) {
oldEndVnode = oldCh[--oldEndIdx]
} else if (newStartVnode == null) {
newStartVnode = newCh[++newStartIdx]
} else if (newEndVnode == null) {
newEndVnode = newCh[--newEndIdx]
// Compare the four cases of start and end nodes
} else if (sameVnode(oldStartVnode, newStartVnode)) {
// 1. Compare the old start node with the new start node
patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue)
oldStartVnode = oldCh[++oldStartIdx]
newStartVnode = newCh[++newStartIdx]
} else if (sameVnode(oldEndVnode, newEndVnode)) {
// 2. Compare the old end node with the new end node
patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue)
oldEndVnode = oldCh[--oldEndIdx]
newEndVnode = newCh[--newEndIdx]
} else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
// 3. Compare the old start node with the new end nodepatchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue) api.insertBefore(parentElm, oldStartVnode.elm! , api.nextSibling(oldEndVnode.elm!) ) oldStartVnode = oldCh[++oldStartIdx] newEndVnode = newCh[--newEndIdx] }else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
// 4. Compare the old end node with the new start nodepatchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue) api.insertBefore(parentElm, oldEndVnode.elm! , oldStartVnode.elm!) oldEndVnode = oldCh[--oldEndIdx] newStartVnode = newCh[++newStartIdx] }else {
// The start node and the end node are different
// Use newStartNode's key to find the same node in the old node array
// Set the key and index objects
if (oldKeyToIdx === undefined) {
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
}
// Iterate over newStartVnode to find the oldVnode index with the same key from the old node
idxInOld = oldKeyToIdx[newStartVnode.key as string]
// If it is a new VNode
if (isUndef(idxInOld)) { // New element
// If not found, newStartNode is the new node
// Create an element to insert into the DOM tree
api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm!)
} else {
// If an old node with the same key is found, record it in elmToMove traversal
elmToMove = oldCh[idxInOld]
if(elmToMove.sel ! == newStartVnode.sel) {// If the old and new nodes have different selectors
// Create a DOM element corresponding to the new start node and insert it into the DOM tree
api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm!)
} else {
// If same, patchVnode()
// Move the elmToMove element to the left
patchVnode(elmToMove, newStartVnode, insertedVnodeQueue)
oldCh[idxInOld] = undefined as any
api.insertBefore(parentElm, elmToMove.elm!, oldStartVnode.elm!)
}
}
// Re-assign newStartVnode to the next new node
newStartVnode = newCh[++newStartIdx]
}
}
// When the loop ends, the old node array completes first or the new node array completes first
if (oldStartIdx <= oldEndIdx || newStartIdx <= newEndIdx) {
if (oldStartIdx > oldEndIdx) {
// If the old node array is traversed first, new nodes are left
// Insert the remaining new nodes to the right
before = newCh[newEndIdx + 1] = =null ? null : newCh[newEndIdx + 1].elm
addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
} else {
// If the new node array is traversed first, the old node is left
// Delete old nodes in batches
removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)
}
}
}
Copy the code