In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-25 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces the Vue in the virtual DOM and Diff algorithm instance analysis related knowledge, the content is detailed and easy to understand, the operation is simple and fast, has a certain reference value, I believe that everyone after reading this Vue in the virtual DOM and Diff algorithm instance analysis article will have a harvest, let's take a look.
A brief introduction to virtual DOM and diff algorithm
Let's start with a simple example of virtual DOM and diff algorithms: for example, there is a household chart, and now we need to modify it as follows
In fact, this is equivalent to a fault-finding game, let's find out the differences with the original. Now, I've circled the differences.
Now, we already know what kind of transformation to be carried out, but how should we do it? The stupidest way is to tear it all down and build it again, but in practice, we certainly will not demolish it and build it again, which is too inefficient and expensive. It is true that the transformation has been completed, but this is not a minimum update, so what we want is diff
So what is diff? In fact, diff in our computer represents a minimum number of updates of an algorithm, will be refined and compared to the minimum amount of updates. In this way, you will find that it is less expensive, less expensive, and more optimized, so the correspondence is very critical in our Vue underlying layer.
All right, now let's go back to our Vue. The above Huxing diagram is equivalent to the DOM nodes in vue. We need to modify these nodes (add, delete and adjust), and then update the DOM with the minimum amount, so as to avoid our performance overhead.
/ / original DOM title 1 2 3 / / modified DOM title Qingfeng 1 2 3 4
Here, we can use the diff algorithm for fine comparison to achieve the minimum amount of updates. Above we know what diff is, and let's take a brief look at what is virtual DOM.
Heading 1 2 3 {sel: "div", elm: undefined, / / indicates that the virtual node has not been on the tree key: undefined, / / unique identification data: {class: {"box": true}}, children: [{sel: "H3" Data: {}, text: "title"}, {sel: "ul", data: {}, children: [{sel: li, data: {}, text: "1"}, {sel: li, data: {}, text: "2"} {sel: li, data: {}, text: "3"}]}
Through observation, it can be found that the virtual DOM is a JavsScript object, which contains sel selectors, data data, text text content, child tags, and so on. This expresses a virtual DOM structure, and the way to deal with the virtual DOM is always simpler and more efficient than the real DOM, so the diff algorithm occurs on the virtual DOM.
Note: the diff algorithm occurs on the virtual DOM.
Why do you need Virtual DOM (virtual DOM)
First of all, we all know that one secret of performance optimization at the front end is to reduce the operation of DOM as much as possible, not only because DOM is relatively slow, but also because changing DOM will cause browser reflow and redrawing, which will degrade performance. Therefore, we need virtual DOM to update the differences to DOM as much as possible in the process of patch (comparing new and old virtual DOM updates to update views). This ensures that DOM will not suffer from poor performance.
Secondly, the use of virtual DOM to change the current state does not need to update the DOM immediately, but updates the updated content, does not do any processing for the content that has not changed, and compares the differences before and after.
Finally, and the original purpose of Virtual DOM, is to better cross-platform, for example, node.js does not have DOM, if you want to implement SSR (server rendering), then one way is to use Virtual DOM, because Virtual DOM itself is a JavaScript object.
H function (create virtual DOM)
Function: h function is mainly used to generate virtual nodes (vnode)
First parameter: label name, component option object, function
Second parameter: attribute corresponding to the tag (optional)
The third parameter: child virtual node, string or array form
H ('props, {props: {href:' http://www.baidu.com'}, 'Baidu'})
The virtual node corresponding to the h function above is:
{sel: 'averse, data: {props: {href:' http://www.baidu.com'}}, text: "Baidu"}
The real DOM nodes are:
Baidu
We can also use h functions nested, such as:
H ('ul', {}, [h (' li', {},'1'), h ('li', {},' 2'), h ('li', {},' 3'),])
Using the h function nested, a virtual DOM tree is generated.
{sel: "ul", elm: undefined, key: undefined, data: {}, children: [{sel: li, elm: undefined, key: undefined, data: {}, text: "1"}, {sel: li, elm: undefined, key: undefined, data: {}, text: "2"} {sel: li, elm: undefined, key: undefined, data: {}, text: "3"}]}
Well, now that we know how the h function is used, let's write a castrated version of the h function by hand.
Handwritten h function
Our handwritten function only considers three cases (with three parameters), which are as follows:
Case ①: h ('div', {},' text') case ②: h ('div', {}, []) case ③: h (' div', {}, h ())
Before writing the h function by hand, we need to declare a function to create a virtual node
/ / vnode.js returns virtual node export default function (sel, data, children, text, elm) {/ / sel selector, data attribute, children child node, text text content, real DOM node bound to elm virtual node const key = data.key return {sel, data, children, text, elm, key}}
After declaring the vnode function, we formally write the h function by hand. The idea is as follows:
Determine whether the third parameter is a string or a number. If it is a string or number, return vnode directly
Determine whether the third parameter is an array. To declare an array to store child nodes, you need to traverse the array, and you need to determine whether each item is an object (because vnode returns an object and must have a sel attribute), but you don't need to execute every item, because the h function has already been executed in the array. In fact, it is not a recursive function call (adjust itself), but a layer of nesting.
Determine whether all three parameters are one object. Assign this object directly to children and return vnode
/ / h.js h function import vnode from ". / vnode" / / situation ①: h ('div', {},' text') / / case ②: h ('div', {}, []) / / case ③: h (' div', {}, h () export default function (sel, data) C) {/ / determine whether to pass three parameters if (arguments.length! = = 3) throw Error ('the passed parameters must be three parameters') / / determine the type of c if (typeof c = 'string' | | typeof c = =' number') {/ / case ① return vnode (sel, data, undefined, c Undefined)} else if (Array.isArray (c)) {/ / case ② / / traversal let children = [] for (let I = 0 I
< c.length; i++) { // 子元素必须是h函数 if (!(typeof c[i] === 'object' && c[i].hasOwnProperty('sel'))) throw Error('数组中有一项不是h函数') // 收集子节点 不需要执行 因为数组里面已经执行h函数来 children.push(c[i]) } return vnode(sel, data, children, undefined, undefined) } else if (typeof c === 'object' && c.hasOwnProperty('sel')) { // 直接将子节点放到children中 let children = [c] return vnode(sel, data, children, undefined, undefined) } else { throw Error('传入的参数格式不对') }} 通过上面的代码,我们已经实现了一个简单 h函数 的基本功能。 感受 diff 算法 在讲解 diff算法 之前,我们先来感受一下 diff算法 的强大之处。先利用 snabbdom 简单来举一个例子。 import { init, classModule, propsModule, styleModule, eventListenersModule, h,} from "snabbdom";//创建出patch函数const patch = init([ classModule, propsModule, styleModule, eventListenersModule, ]);//让虚拟节点上树const container = document.getElementById("container");const btn = document.getElementById("btn");//创建虚拟节点const myVnode1 = h('ul', {}, [ h('li', {}, 'A'), h('li', {}, 'B'), h('li', {}, 'C'), h('li', {}, 'D')])patch(container, myVnode1)const myVnode2 = h('ul', {}, [ h('li', {}, 'A'), h('li', {}, 'B'), h('li', {}, 'C'), h('li', {}, 'D'), h('li', {}, 'E'),])btn.addEventListener('click', () =>{/ / Tree patch (myVnode1,myVnode2)})
When we click on the change DOM, we find that a new li tag content is E, and it is difficult to see whether to replace all the old virtual DOM with the new virtual DOM, and then render it into a real DOM, or directly add a node to the old virtual DOM, so here we can skillfully open the test tool and modify the tag content directly. If all is removed after clicking, the content of the tag will be changed, and if the content has not changed, it will be added last.
Click to change the DOM structure:
Sure enough, the content of the previous modification has not changed, which can be verified that the refinement of the diff algorithm is compared to update in a minimum amount. So the question is, what if I add a node in front of it? Do you also add a node directly in front of it, just like you did at the end? We might as well try it and see how it works:
.. const container = document.getElementById ("container"); const btn = document.getElementById ("btn") / / create virtual node const myVnode1 = h ('ul', {}, [h (' li', {},'A'), h ('li', {},' B'), h ('li', {},' C'), h ('li', {},' D')) patch (container, myVnode1) const myVnode2 = h ('ul', {}, [h (' li', {}) 'E'), / / move E to the front h (' li', {},'A'), h ('li', {},' B'), h ('li', {},' C'), h ('li', {},' D'),] btn.addEventListener ('click', () = > {/ / tree patch (myVnode1,myVnode2)}))
Click to change the DOM structure
Oh, ho! Unlike what we thought, you will find that all the text content has changed, that is, all the previous DOM will be removed and the new one will be re-uploaded to the tree. At this time, you are not in doubt that the diff algorithm is not so powerful, but you are very wrong to think so. Recall that in the process of learning Vue, when traversing DOM nodes, there is no special emphasis on writing the key unique identifier. At this time, key plays its role here. Let's take a look at the effect with key:
Const myVnode1 = h ('ul', {}, [h (' li', {key: "A"},'A'), h ('li', {key: "B"},' B'), h ('li', {key: "C"},' C'), h ('li', {key: "D"},' D')]) patch (container) MyVnode1) const myVnode2 = h ('ul', {}, [h (' li', {key: "E"},'E'), h ('li', {key: "A"},' A'), h ('li', {key: "B"},' B'), h ('li', {key: "C"},' C'), h ('li') {key: "D"},'D'),]).
Click to change the DOM structure
Seeing the above results, at this moment, do you suddenly realize what role key plays in the cycle? The first conclusion we can draw is that key is the unique identity of the current node, telling the diff algorithm that they are the same DOM node before and after the change.
When we modify the parent node, the parent node of the new and old virtual DOM is not the same node. Let's take a look at how the diff algorithm is analyzed.
Const myVnode1 = h ('ul', {}, [h (' li', {key: "A"},'A'), h ('li', {key: "B"},' B'), h ('li', {key: "C"},' C'), h ('li', {key: "D"},' D')] patch (container, myVnode1) const myVnode2 = h ('ol') {}, [h ('li', {key: "A"},' A'), h ('li', {key: "B"},' B'), h ('li', {key: "C"},' C'), h ('li', {key: "D"},' D'),])
Click to change the structure of DOM
You will find that here all the old nodes are removed, and then the new nodes are re-treed. The second conclusion we can draw is:
Only if it is the same virtual node, the diff algorithm makes fine comparison, otherwise it will violently delete the old one and insert the new one. The basis for judging the same virtual node: the selector (sel) is the same and key is the same.
What if it is the same virtual node, but the child nodes are not being compared at the same layer?
Const myVnode1 = h ('div', {}, [h (' li', {key: "A"},'A'), h ('li', {key: "B"},' B'), h ('li', {key: "C"},' C'), h ('li', {key: "D"},' D')] patch (container, myVnode1) const myVnode2 = h ('div') {}, h ('section', {}, [h (' li', {key: "A"},'A'), h ('li', {key: "B"},' B'), h ('li', {key: "C"},' C'), h ('li', {key: "D"},' D'),])
Click to change the DOM structure
You will find that the DOM structure is wrapped with an extra layer of section tags, and then the content of the text has changed, so we can conclude that conclusion 3:
The diff algorithm only makes the same layer comparison, not the cross-layer comparison. Even if it is the same virtual node, but across layers, instead of fine comparison, it violently deletes the old one and then inserts the new one.
In summary, we draw three conclusions of diff algorithm:
Key is the unique identity of the current node, telling the diff algorithm that they are the same DOM node before and after the change.
Only if it is the same virtual node, the diff algorithm makes fine comparison, otherwise it will violently delete the old one and insert the new one. The basis for judging the same virtual node: the selector (sel) is the same and key is the same.
The diff algorithm only makes the same layer comparison, not the cross-layer comparison. Even if it is the same virtual node, but across layers, instead of fine comparison, it violently deletes the old one and then inserts the new one.
Seeing this, I believe you have gained a lot from the diff algorithm.
Patch function
The main function of the patch function is to determine whether it is the same node type, is to make a fine comparison, not to delete it violently and insert a new one.
We can simply draw the main flow chart of the patch function as follows:
/ / patch.js patch function import vnode from ". / vnode"; import sameVnode from ". / sameVnode"; import createElement from ". / createElement"; export default function (oldVnode, newVnode) {/ / determine whether oldVnode is a virtual node if (oldVnode.sel = ='| oldVnode.sel = = undefined) {/ / console.log ('not a virtual node') / / create virtual DOM oldVnode = emptyNodeAt (oldVnode)} / / determine whether it is the same node if (sameNode (oldVnode, newVnode)) {console.log ('same node') } else {/ / violently delete the old node, insert the new node / / pass in two parameters, and insert the created node into the position of the specified benchmarking createElement (newVnode, oldVnode.elm)}} / / create a virtual DOMfunction emptyNodeAt (elm) {return vnode (elm.tagName.toLowerCase (), {}, [], undefined, elm)}
Before going up the DOM tree, we need to understand the insertBefore () method and the appendChild () method in DOM, because only if you really know how to use both of them will make you clearer when you hand-write the tree below.
AppendChild () method
The appendChild () method: you can add a new child node to the end of the list of child nodes of a node. For example: appendChild (newchild).
Note: the appendChild () method adds a new node to the end of the child node in the parent node. (relative to the parent node).
Qingfeng 12 const box = document.querySelector ('.box') const appendDom = document.createElement ('div') appendDom.style.backgroundColor =' blue' appendDom.style.height = 100 + 'px' appendDom.style.width = 100 +' px' / / add a div box.appendChild (appendDom) to the end of the box
You will find that the created div is nested in box, div belongs to the child node of box, and box is the child node of div.
InsertBefore () method
The insertBefore () method: you can insert a new child node in front of an existing word node. For example: insertBefore (newchild,rechild).
Note: the insertBefore () method adds a new node in front of an existing node. (as opposed to child nodes).
Qingfeng 12 const box = document.querySelector ('.box') const insertDom = document.createElement ('p') insertDom.innerText ='I am insertDOM' / / add a new node box [XSS _ clean] .insertBefore (insertDom, box) before box in body
We found that box and div are on the same layer and belong to sibling nodes.
Dealing with different nodes
SameVnode function
Function: compare whether two nodes are the same node
/ / sameVnode.jsexport default function sameVnode (oldVnode, newVnode) {return (oldVnode.data? OldVnode.data.key: undefined) = (newVnode.data? NewVnode.data.key: undefined) & & oldVnode.sel = = newVnode.sel}
Handwritten on a tree for the first time
After understanding the appendChild () method and insertBefore () method above, we officially started to put the real DOM on the tree and render the page.
/ / patch.js patch function import vnode from ". / vnode"; import sameVnode from ". / sameVnode"; import createElement from ". / createElement"; export default function (oldVnode, newVnode) {/ / determine whether oldVnode is a virtual node if (oldVnode.sel = ='| oldVnode.sel = = undefined) {/ / console.log ('not a virtual node') / / create virtual DOM oldVnode = emptyNodeAt (oldVnode)} / / determine whether it is the same node if (sameNode (oldVnode, newVnode)) {console.log ('same node') } else {/ / violently delete the old node, insert the new node / / pass in two parameters, and insert the created node into the position of the specified benchmarking createElement (newVnode, oldVnode.elm)}} / / create a virtual DOMfunction emptyNodeAt (elm) {return vnode (elm.tagName.toLowerCase (), {}, [], undefined, elm)}
As we clearly know above, the role of patch is to determine whether it is the same node, so we need to declare a createElement function to create a real DOM.
CreateElement function
CreateElement is mainly used to create the real DOM of child nodes.
/ / createElement.jsexport default function createElement (vnode Pivot) {/ / create the node on the tree let domNode = document.createElement (vnode.sel) / / determine whether there is text content or a child node if (vnode.text! =''& & (vnode.children = = undefined | | vnode.children.length = = 0)) {/ / the text content is directly assigned to domNode.innerText = vnode.text / / add to the body on the tree Add node / / insertBefore () method: you can insert a new child node in front of an existing word node. Insert [XSS _ clean] .insertBefore (domNode, pivot)} else if (Array.isArray (vnode.children) & & vnode.children.length > 0) {/ / have child nodes}} / / index.jsimport patch from ". / mysnabbdom/patch"; import h from'. / mysnabbdom/h'const container = document.getElementById ("container") / / create a virtual node const myVnode1 = h ('H2, {}, 'text') patch (container, myVnode1)
We have successfully rendered the real DOM to the page, but this is only the simplest case, that is, the third parameter of the h function is a string, so, when the third parameter is an array, it is impossible to climb the tree, so we need to further optimize the createElement function to achieve recursive tree.
Recursively create child nodes
We find that when we first climb the tree, the createElement function has two parameters, namely: newVnode (the new virtual DOM), benchmarking (which is used to insert the tree into a node). Inside the createElement, we use the insertBefore () method to climb the tree. Using this method, we need to know which node the existing node is, of course, when there is a text (the third parameter is a string or number). We can find the location to insert, but when there is a children (child node), we can not determine the location of the pole, so we have to put the work on the tree into the patch function, that is, the createElement function is only responsible for creating nodes.
/ / index.jsimport patch from ". / mysnabbdom/patch"; import h from'. / mysnabbdom/h'const container = document.getElementById ("container") / / create virtual node const myVnode1 = h ('ul', {}, [h (' li', {},'A'), h ('li', {},' B'), h ('li', {},' C'), h ('li', {},' D')] patch (container, myVnode1) / / patch.jsimport vnode from ". / vnode"; import sameVnode from ". / sameVnode" Import createElement from ". / createElement"; export default function (oldVnode, newVnode) {/ / determine whether oldVnode is a virtual node if (oldVnode.sel = =''| | oldVnode.sel = = undefined) {/ / console.log ('not a virtual node') / / create virtual DOM oldVnode = emptyNodeAt (oldVnode)} / / determine whether it is the same node if (sameNode (oldVnode, newVnode)) {console.log ('same node') } else {/ / violently deletes the old node and inserts the new node / / the input parameter returns a real DOM let newVnodeElm = createElement (newVnode) console.log (newVnodeElm) for the created virtual DOM node / / oldVnode.elm [XSS _ clean] adds a new node if (oldVnode.elm [XSS _ clean] & & oldVnode.elm) in front of the old node in the body for body {oldVnode.elm [XSS _ clean] .insertBefore (newVnodeElm OldVnode.elm)} / / Delete the old node oldVnode.elm [XSS _ clean] .removeChild (oldVnode.elm)} / / create a virtual DOMfunction emptyNodeAt (elm) {return vnode (elm.tagName.toLowerCase (), {}, [], undefined, elm)}
Perfect createElement function
/ / createElement.js is only responsible for creating the real node export default function createElement (vnode) {/ / creating the node on the tree let domNode = document.createElement (vnode.sel) / / determining whether there is text content or child node if (vnode.text! =''& & (vnode.children = = undefined | | vnode.children.length = = 0)) {/ / the text content is directly assigned to domNode .innerText = vnode.text / / add a node to the body on the tree / / insertBefore () method: you can insert a new child node in front of the existing word node. } else if (Array.isArray (vnode.children) & & vnode.children.length > 0) {/ / with child node for (let I = 0; I) relative to the child node
< vnode.children.length; i++) { // console.log(vnode.children[i]); let ch = vnode.children[i] // 进行递归 一旦调用createElement意味着 创建了DOM 并且elm属性指向了创建好的DOM let chDom = createElement(ch) // 添加节点 使用appendChild 因为遍历下一个之前 上一个真实DOM(这里的domVnode)已经生成了 所以可以使用appendChild domNode.appendChild(chDom) } } vnode.elm = domNode return vnode.elm} 经过上面的分析,我们已经完成了对createElem函数的完善,可能你对这个递归有点不了解,那么大概捋一下进行的过程: 首先,一开始的这个 新的虚拟DOM的sel 属性为 ul,创建的真实DOM节点为 ul,执行 createElement函数 发现,新的虚拟DOM里面有children属性,children 属性里面又包含 h函数。 其次,进入到for循环中,拿到 children 中的第一项,然后再次 调用crateElement函数 创建真实DOM,上面第一次调用createElement的时候已经创建了ul,执行完第一项返回创建的虚拟DOM,然后使用 appendChild方法()追加到 ul中,依次类推,执行后面的数组项。 最后,将创建好的 所有真实DOM 返回出去,在 patch函数 中上树。 执行上面的代码,测试结果如下: 完美!我们成功的将递归子节点完成了,无论嵌套多少层,我们都可以通过递归将子节点渲染到页面上。 前面,我们实现了不是同一个节点的时候,进行删除旧节点和插入新节点的操作,下面,我们来实现是相同节点时的相关操作,这也是文章中最重要的部分,diff算法 就包含在其中!!! 处理相同节点 上面的 patch函数 流程图中,我们已经处理了不同节点的时候,进行暴力删除旧的节点,然后插入新的节点,现在我们进行处理相同节点的时候,进行精细化的比较,继续完善 patch函数 的主流程图: 看到上面的流程图,你可能会有点疑惑,为什么不在 newVnode 是否有 Text属性 中继续判断 oldVnode 是否有 children 属性而是直接判断两者之间的 Text 是否相同,这里需要提及一个知识点,当我们进行 DOM操作的时候,文本内容替换DOM的时候,会自动将DOM结构全部销毁掉,innerText改变了,DOM结构也会随之被销毁,所以这里可以不用判断 oldVnode 是否存在 children 属性,如果插入DOM节点,此时的Text内容并不会被销毁掉,所以我们需要手动的删除。这也是为什么在流程图后面,我们添加 newVnode 的children 的时候需要将 oldVnode 的 Text 手动删除,而将 newVnode 的 Text 直接赋值给oldVnode.elm.innerText 的原因。 知道上面流程图是如何工作了,我们继续来书写patch函数中是同一个节点的代码。 // patch.jsimport vnode from "./vnode";import sameVnode from "./sameVnode";import createElement from "./createElement";export default function (oldVnode, newVnode) { // 判断oldVnode是否是虚拟节点 if (oldVnode.sel == '' || oldVnode.sel == undefined) { // console.log('不是虚拟节点'); // 创建虚拟DOM oldVnode = emptyNodeAt(oldVnode) } // 判断是否是同一个节点 if (sameNode(oldVnode, newVnode)) { console.log('是同一个节点'); // 是否是同一个对象 if (oldVnode === newVnode) return // newVnode是否有text if (newVnode.text && (newVnode.children == undefined || newVnode.children.length == 0)) { // 判断newVnode和oldVnode的text是否相同 if (!(newVnode.text === oldVnode.text)) { // 直接将text赋值给oldVnode.elm.innerText 这里会自动销毁oldVnode的cjildred的DOM结构 oldVnode.elm.innerText = newVnode.text } // 意味着newVnode有children } else { // oldVnode是否有children属性 if (oldVnode.children != undefined && oldVnode.children.length >0) {/ / oldVnode has children attribute} else {/ / oldVnode does not have children attribute / / manually delete oldVnode's text oldVnode.elm.innerText =''/ / traversing for (let I = 0; I
< newVnode.children.length; i++) { let dom = createElement(newVnode.children[i]) // 追加到oldvnode.elm中 oldVnode.elm.appendChild(dom) } } } } else { // 暴力删除旧节点,插入新的节点 // 传入参数为创建的虚拟DOM节点 返回以一个真实DOM let newVnodeElm = createElement(newVnode) console.log(newVnodeElm); // oldVnode.elm[xss_clean] 为body 在body中 在旧节点的前面添加新的节点 if (oldVnode.elm[xss_clean] && oldVnode.elm) { oldVnode.elm[xss_clean].insertBefore(newVnodeElm, oldVnode.elm) } // 删除老节点 oldVnode.elm[xss_clean].removeChild(oldVnode.elm) }}// 创建虚拟DOMfunction emptyNodeAt(elm) { return vnode(elm.tagName.toLowerCase(), {}, [], undefined, elm)}....//创建虚拟节点const myVnode1 = h('ul', {}, 'oldVnode有text')patch(container, myVnode1)const myVnode2 = h('ul', {}, [ h('li', {}, 'A'), h('li', {}, 'B'), h('li', {}, 'C'), h('li', {}, 'D')])btn.addEventListener('click', () =>{patch (myVnode1, myVnode2)})
The effects of oldVnode with tex attribute and newVnode with children attribute are as follows:
Create virtual node const myVnode1 = h ('ul', {}, [h (' li', {},'A'), h ('li', {},' B'), h ('li', {},' C'), h ('li', {},' D')]) patch (container, myVnode1) const myVnode2 = h ('ul', {}) 'newVnode has text') btn.addEventListener (' click', () = > {patch (myVnode1, myVnode2)})
The effects of oldVode with children attribute and newVnode with text attribute are as follows:
Perfect! Now all we need is the last diff.
PatchVnode function
In the patch function, we need to divide the comparison with the same node into a separate module patchVnode function, which is convenient for us to carry out recursive operations in the diff algorithm.
The main functions of the patchVnode function are:
Determine whether newVnode and oldVnode point to the same object, and if so, directly return
If they all have text and are not equal, or if oldVnode has child nodes and newVnode does not, set the text node of oldVnode.elm to the text node of newVnode.
If oldVnode does not have child nodes and newVnode does, then add the child nodes of newVnode after oldVnode.elm, and then delete the text of oldVnode.elm
If both have child nodes, it is important to execute the updateChildren function to compare the child nodes
/ / patchVnode.jsexport default function patchVnode (oldVnode NewVnode) {/ / whether it is the same object if (oldVnode = = newVnode) return / / newVnode has text if (newVnode.text & & (newVnode.children = = undefined | | newVnode.children.length = = 0)) {/ / determine whether the text of newVnode and oldVnode are the same if (! (newVnode.text = = oldVnode.text)) {/ / assign text directly to oldVnode.elm.innerText Here, the DOM structure of oldVnode's cjildred oldVnode.elm.innerText = newVnode.text} / / indicates that newVnode has children} else {/ / oldVnode has children attribute if (oldVnode.children! = undefined & & oldVnode.children.length > 0) {/ / oldVnode has children attribute} else {/ / oldVnode does not have children attribute Sex / / manually delete text oldVnode.elm.innerText of oldVnode =''/ / traverse for (let I = 0 I < newVnode.children.length; iTunes +) {let dom = createElement (newVnode.children [I]) / append to oldvnode.elm oldVnode.elm.appendChild (dom)}
Diff algorithm
Refined comparison: four optimization strategies of diff algorithm. Here we compare the diff algorithm in the form of double pointers, namely, the old front pointer, the old back pointer, the new front pointer and the new rear pointer. (the front pointer moves down and the back pointer moves up)
Four optimization strategies: (hit: both key and sel should be the same)
①, New Front and Old Front
②, new queen and old queen
③, New Post and Old Front
④, before and after the new
Note: the second is taken only when the first is missed, and so on, and if all four are missed, you need to find it through a loop. The pointer will not move until it is hit, otherwise it will not move.
①, New Front and Old Front
If the cycle of the old node is finished first, it means that there are nodes that need to be inserted in the new node.
②, new queen and old queen
If the new node completes the cycle first, the old node has remaining nodes, indicating that there are nodes in the old node that need to be deleted.
Multi-deletion case: when only case ① is hit and the remaining three are missed, you need to loop through to find the corresponding node in the old node, and then set this node to undefined in the old virtual node. The deleted node is between the old before and after (including the old before and after).
③, New Post and Old Front
When the ③ hits the new back and the old front, you need to move the node, move the new rear pointing node to the back of the old node, and find the corresponding node in the old node, and then set the node to undefined in the old virtual node.
④, before and after the new
When the ④ hits the new front and the old back, move the node to the front of the old node, find the corresponding node in the old node, and set the node to undefined in the old virtual node.
Well, after the above four hit methods explained dynamically, the dynamic gif image has a watermark, which may not be very comfortable to look at, but of course being able to understand is the most important, so let's start writing the code of the diff algorithm by hand.
UpdateChildren function
The main purpose of the updateChildren () method is to refine the comparison, and then update the child nodes. There is a lot of code here, so you need to read it patiently.
Import createElement from ". / createElement"; import patchVnode from ". / patchVnode"; import sameVnode from ". / sameVnode"; export default function updateChildren (parentElm, oldCh, newCh) {/ / parentElm parent node location is used to move the node oldCh old node children newCh new node children / / console.log (parentElm, oldCh, newCh) / / old front let oldStartIndex = 0 / / old rear let oldEndIndex = oldCh.length-1 / / new front let newStartIndex = 0 / / old back let newEndIndex = newCh.length-1 / / old front node let oldStartVnode = oldCh [0] / / old rear node let oldEndVnode = oldCh [oldEndIndex] / / new front node let newStartVnode = newCh [0] / / new Back node let newEndVnode = newCh [newEndIndex] / / Storage mapkey let keyMap / / before the loop condition is old
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.