-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
71 lines (38 loc) · 32.4 KB
/
atom.xml
File metadata and controls
71 lines (38 loc) · 32.4 KB
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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Xhiteam's Blog</title>
<subtitle>Believe in Technology</subtitle>
<link href="https://xhiteam.github.io/atom.xml" rel="self"/>
<link href="https://xhiteam.github.io/"/>
<updated>2021-02-02T08:16:42.909Z</updated>
<id>https://xhiteam.github.io/</id>
<author>
<name>xhiteam</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>Vue虚拟DOMdiff算法</title>
<link href="https://xhiteam.github.io/2020/10/16/Vue%E8%99%9A%E6%8B%9FDOMdiff%E7%AE%97%E6%B3%95/"/>
<id>https://xhiteam.github.io/2020/10/16/Vue%E8%99%9A%E6%8B%9FDOMdiff%E7%AE%97%E6%B3%95/</id>
<published>2020-10-16T14:29:16.000Z</published>
<updated>2021-02-02T08:16:42.909Z</updated>
<content type="html"><![CDATA[<p><strong>前言:当数据发生变化时,vue是如何去更新节点的?</strong></p><p>渲染真实的DOM开销是很大的,如果当修改了某个数据后直接渲染到真实dom上回引起整个dom数的重绘和重排,下例展示了一个“昂贵的”DOM:<br>控制台输入:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">var odiv = document.createElement('div');</span><br><span class="line">for(var k in odiv ){</span><br><span class="line"> console.log(k)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>结果:<br><img src="http://doc.xhiteam.com/Public/Uploads/2020-06-07/5edcebe033a5a.png"></p><p>可见,真正的DOM元素是非常庞大的,因为浏览器把DOM设计地非常复杂,所以当我们频繁地去更新DOM时,如果每次都重新生成新的元素,对性能是巨大的浪费。那我们更新DOM时,能不能只更新修改的地方呢?此时diff算法便能够帮助我们</p><p>diff会先根据真实DOM生成一棵virtual DOM(虚拟dom),当virtual DOM某个节点的数据改变后会生成一个新的Vnode,然后Vnode和oldVnode作对比,发现有不一样的地方就直接修改在真实的DOM上,然后使oldVnode的值为Vnode。<br>#1.diff算法概念<br>###1.1简介<br>diff 算法是一种通过同层的树节点进行比较的高效算法,避免了对树进行逐层搜索遍历,所以时间复杂度只有 O(n)。diff 算法的在很多场景下都有应用,例如在 vue 虚拟 dom 渲染成真实 dom 的新旧 VNode 节点比较更新时,就用到了该算法。<br>###1.2特点<br>只会在同层级进行,不会跨层级比较<br><img src="http://doc.xhiteam.com/Public/Uploads/2020-06-07/5edcead7e8729.png"><br>eg.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">//old:</span><br><span class="line"><div></span><br><span class="line"> <p>123</p></span><br><span class="line"></div></span><br><span class="line">//new:</span><br><span class="line"><div></span><br><span class="line"> <span>456</span></span><br><span class="line"></div></span><br></pre></td></tr></table></figure><p>上面的代码会分别比较同一层的两个div以及第二层的p和span,但是不会拿div和span作比较。</p><p>#2.Virtual DOM<br> Virtual DOM也就是虚拟节点,其实就是一棵以 JavaScript 对象( VNode 节点)作为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。最终可以通过一系列操作使这棵树映射到真实环境上<br> 简单来说,可以把Virtual DOM 理解为一个简单的JS对象,并且最少包含标签名( tag)、属性(attrs)和子元素对象( children)三个属性。不同的框架对这三个属性的命名会有点差别。</p><p>eg.<br>现在有这样一个Vritual DOM:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"> {</span><br><span class="line"> tag: 'div'</span><br><span class="line"> data: {</span><br><span class="line"> class: 'outer'</span><br><span class="line"> },</span><br><span class="line"> children: [</span><br><span class="line"> {</span><br><span class="line"> tag: 'div',</span><br><span class="line"> data: {</span><br><span class="line"> class: 'inner'</span><br><span class="line"> }</span><br><span class="line"> text: 'Virtual DOM'</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>渲染之后的真实DOM为:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><div class="outer"></span><br><span class="line"> <span class="inner">Virtual DOM</span></span><br><span class="line"></div></span><br></pre></td></tr></table></figure><p>#3.diff算法思想<br>重要的几个函数<br>###sameVnode<br>比较了两个节点的key,tag(标签),isComment(是否为注释节点)data(注意这里的data指的是VNodeData),input的话直接比较type。<br> (key相同,节点的类型相同–说明这个节点只是变化了内容,不需要创建新的节点,可以复用)<br><img src="http://doc.xhiteam.com/Public/Uploads/2020-06-07/5edceda231f37.png"></p><pre><code>简单的举个的case,比如之前是一个<div>标签,由于逻辑的变动,变为<p>标签了,则sameVnode会返回false(a.tag === b.tag 返回 false)。所以sameVnode表明的是,满足以上条件就是同一个元素,才可进行patchVnode。反过来理解就是,只要以上任意一个发生改变,则无需进行pathchVnode,直接根据vnode进行createElm即可。注意,sameVnode 返回true,不能说明是同一个vnode,这里的相同是指当前的以上指标一致,他们的children可能发生了变化,仍需进行patchVnode进行更新。</code></pre><p>###patchVnode<br>该方法用来真正对新旧节点进行对比,得到最小应该变化的DOM,然后直接更新DOM。下面是需要patch的几种情况,这几种情况都会有对应的真实DOM测试用例来验证。<br>整体策略:深度优先,同层比较</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line">function patchVnode(oldVnode,vnode,insertedVnodeQueue,ownerArray,index,removeOnly) </span><br><span class="line">{</span><br><span class="line">// ...</span><br><span class="line"> // 新旧节点完全一致,直接返回</span><br><span class="line">if (oldVnode === vnode) {</span><br><span class="line">return</span><br><span class="line">}</span><br><span class="line"> // 将旧节点上的 DOM,添加到新节点上。之后新旧节点若有不一致,直接修改 elm 即可</span><br><span class="line">const elm = vnode.elm = oldVnode.elm</span><br><span class="line"></span><br><span class="line">const oldCh = oldVnode.children</span><br><span class="line">const ch = vnode.children</span><br><span class="line">// 新节点不是文本节点</span><br><span class="line">if (isUndef(vnode.text)) {</span><br><span class="line"> if (isDef(oldCh) && isDef(ch)) {</span><br><span class="line"> // 新旧节点都存在子元素,更新子节点(递归操作)</span><br><span class="line"> if (oldCh !== ch) </span><br><span class="line"> updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)</span><br><span class="line"> } else if (isDef(ch)) {</span><br><span class="line"> // 只有新节点存在子元素,先清空节点上的文本,然后将子元素创建为真实 DOM 插入</span><br><span class="line"> /*</span><br><span class="line"> old:</span><br><span class="line"> <div></span><br><span class="line"> </div></span><br><span class="line"> new:</span><br><span class="line"> <div></span><br><span class="line"> <p></p></span><br><span class="line"> </div></span><br><span class="line"> */</span><br><span class="line"> if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')</span><br><span class="line"> addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)</span><br><span class="line"> } else if (isDef(oldCh)) {</span><br><span class="line"> // 只有旧节点有子元素,直接删除</span><br><span class="line"> /*</span><br><span class="line"> old:</span><br><span class="line"> <div></span><br><span class="line"> <p></p></span><br><span class="line"> </div></span><br><span class="line"> new:</span><br><span class="line"> <div></span><br><span class="line"> </div></span><br><span class="line"> </span><br><span class="line"> */</span><br><span class="line"> removeVnodes(oldCh, 0, oldCh.length - 1)</span><br><span class="line"> } else if (isDef(oldVnode.text)) {</span><br><span class="line"> // 只有旧节点上有文本时:新节点上没有,清空旧节点上的文本</span><br><span class="line"> nodeOps.setTextContent(elm, '')</span><br><span class="line"> }</span><br><span class="line"> } </span><br><span class="line"> else if (oldVnode.text !== vnode.text) {</span><br><span class="line"> //新节点是文本节点,判断与旧节点上的文本节点是否一致。不一致更新节点上的文本。</span><br><span class="line"> nodeOps.setTextContent(elm, vnode.text)</span><br><span class="line"> }</span><br><span class="line"> //...</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="http://doc.xhiteam.com/Public/Uploads/2020-06-07/5edcee3a55709.png"><br>###updateChildren<br>比较新旧子节点</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line">function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {</span><br><span class="line"> // 四个指针</span><br><span class="line"> let oldStartIdx = 0 // 旧节点第一个元素的下标</span><br><span class="line"> let newStartIdx = 0 // 新节点第一个元素的下标</span><br><span class="line"> let oldEndIdx = oldCh.length - 1 // 旧节点最后一个元素的下标</span><br><span class="line"> let oldStartVnode = oldCh[0] // 旧节点中第一个节点</span><br><span class="line"> // 四个节点</span><br><span class="line"> let oldEndVnode = oldCh[oldEndIdx] // 旧节点最后一个节点</span><br><span class="line"> let newEndIdx = newCh.length - 1 // 新节点最后一个元素的下标</span><br><span class="line"> let newStartVnode = newCh[0] // 新节点第一个节点</span><br><span class="line"> let newEndVnode = newCh[newEndIdx] // 新节点最后一个节点</span><br><span class="line"> let oldKeyToIdx, idxInOld, vnodeToMove, refElm</span><br><span class="line">//这里是 diff 算法的核心流程,分情况进行了新老节点的比较并移动对应的 VNode 节点,while 循环的退出条件是直到老节点或者新节点的开始位置大于结束位置。</span><br><span class="line"> while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {</span><br><span class="line"> // 如果旧节点中开始的节点是 undefined,开始节点下标就后移一位</span><br><span class="line"> if (isUndef(oldStartVnode)) {</span><br><span class="line"> oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left</span><br><span class="line"> } </span><br><span class="line"> else if (isUndef(oldEndVnode)) {</span><br><span class="line"> // 如果旧节点结束节点是 undefined,结束节点下标就迁移一位</span><br><span class="line"> oldEndVnode = oldCh[--oldEndIdx]</span><br><span class="line"> } </span><br><span class="line"> else if (sameVnode(oldStartVnode, newStartVnode)) {</span><br><span class="line"> // 旧开始节点与新开始节点相同,需要比较他们的子节点</span><br><span class="line"> patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)</span><br><span class="line"> // 之后旧开始节点、新开始节点,下标均后移一位</span><br><span class="line"> oldStartVnode = oldCh[++oldStartIdx]</span><br><span class="line"> newStartVnode = newCh[++newStartIdx]</span><br><span class="line"> } </span><br><span class="line"> else if (sameVnode(oldEndVnode, newEndVnode)) {</span><br><span class="line"> // 旧结束节点与新结束节点相同,需要比较他们的子节点</span><br><span class="line"> patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)</span><br><span class="line"> // 之后旧结束节点、新结束节点,下标均前移一位</span><br><span class="line"> oldEndVnode = oldCh[--oldEndIdx]</span><br><span class="line"> newEndVnode = newCh[--newEndIdx]</span><br><span class="line"> } </span><br><span class="line"> else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right</span><br><span class="line"> // 旧开始节点与新结束节点相同,这说明这次数据更新后 oldStartVnode 已经跑到了 oldEndVnode 后面去了。这时候在 patchVnode 后,还需要将当前真实 dom 节点移动到 oldEndVnode 的后面,同时老 VNode 节点开始索引加 1,新 VNode 节点的结束索引减 1。</span><br><span class="line"> patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)</span><br><span class="line"> // 旧开始节点插入到旧结束节点后面</span><br><span class="line"> canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))</span><br><span class="line"> // 旧节点开始下标后移一位,新节点结束下标前移一位</span><br><span class="line"> oldStartVnode = oldCh[++oldStartIdx]</span><br><span class="line"> newEndVnode = newCh[--newEndIdx]</span><br><span class="line"> } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left</span><br><span class="line"> // 旧结束节点与新开始节点相同时,这说明这次数据更新后 oldEndVnode 跑到了 oldStartVnode 的前面去了。这时候在 patchVnode 后,还需要将当前真实 dom 节点移动到 oldStartVnode 的前面,同时老 VNode 节点结束索引减 1,新 VNode 节点的开始索引加 1</span><br><span class="line"> patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)</span><br><span class="line"> // 将旧结束节点插入到旧开始节点之前</span><br><span class="line"> canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)</span><br><span class="line"> // 旧结束节点前移一位,新开始节点后移一位</span><br><span class="line"> oldEndVnode = oldCh[--oldEndIdx]</span><br><span class="line"> newStartVnode = newCh[++newStartIdx]</span><br><span class="line"> } </span><br><span class="line"> else {</span><br><span class="line"> // 否则,将每个旧节点的 key 值组成一个对应的 map 表,然后判断新节点的 key 是否在 map 表中</span><br><span class="line"> if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)</span><br><span class="line"> // idxInOld 是在旧节点列表中,与新节点相同的旧节点位置</span><br><span class="line"> idxInOld = isDef(newStartVnode.key)</span><br><span class="line"> ? oldKeyToIdx[newStartVnode.key] // key 值比较</span><br><span class="line"> : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx) // sameVnode 进行比较</span><br><span class="line"> if (isUndef(idxInOld)) { // New element</span><br><span class="line"> // 如果 key 不在 map 表中,则创建新节点</span><br><span class="line"> createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)</span><br><span class="line"> } else {</span><br><span class="line"> // 若在,则判断该 key 值对应的旧节点与新节点是否是相同的节点</span><br><span class="line"> vnodeToMove = oldCh[idxInOld]</span><br><span class="line"> if (sameVnode(vnodeToMove, newStartVnode)) {</span><br><span class="line"> // 若该 key 值对应的旧节点与新节点是相同的节点,则比较他们的子节点</span><br><span class="line"> // 同时将该 key 值对应的节点插入到旧开始节点之前</span><br><span class="line"> patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)</span><br><span class="line"> oldCh[idxInOld] = undefined</span><br><span class="line"> canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)</span><br><span class="line"> } else {</span><br><span class="line"> // 若不相同,则创建新节点</span><br><span class="line"> // same key but different element. treat as new element</span><br><span class="line"> createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> // key 值判断之后,新开始节点后移一位</span><br><span class="line"> newStartVnode = newCh[++newStartIdx]</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> if (oldStartIdx > oldEndIdx) {</span><br><span class="line"> // 如果旧节点列表先处理完,则将剩余的新节点创建为真实 DOM 插入</span><br><span class="line"> refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm</span><br><span class="line"> addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)</span><br><span class="line"> } else if (newStartIdx > newEndIdx) {</span><br><span class="line"> // 如果新节点列表先处理完,则删除剩余的旧节点</span><br><span class="line"> removeVnodes(oldCh, oldStartIdx, oldEndIdx)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>分为5种情况:</p><p>1、旧开始节点 == 新开始节点<br>若旧开始节点与新开始节点相等时,说明旧开始节点的位置是对的,不需要更新该节点。之后是将旧开始节点和新开始节点的下标后移一位。<br><img src="http://doc.xhiteam.com/Public/Uploads/2020-06-07/5edceebd9e212.png"><br>2、旧结束节点 == 新结束节点<br>若旧结束节点与新结束节点相等,说明旧节点的位置是对的,不需要更新该节点。之后是将旧结束节点和新结束节点的下标前移一位。<br><img src="http://doc.xhiteam.com/Public/Uploads/2020-06-07/5edceecc1f607.png"><br>3、旧开始节点 == 新结束节点<br>若旧开始节点与新结束节点相等,说明旧开始节点的位置不对了,需要移动到oldEndVnode后面。然后将旧开始节点的下标后移一位,新结束节点的下标前移一位<br><img src="http://doc.xhiteam.com/Public/Uploads/2020-06-07/5edceef00a9be.png"><br>4、旧结束节点 == 新开始节点<br>若旧结束节点与新开始节点相等,说明旧结束节点需要移动到oldStartVnode前面。然后将旧结束节点前移一位,新开始节点位置后移一位。<br><img src="http://doc.xhiteam.com/Public/Uploads/2020-06-07/5edcef25228fa.png"><br>5、key 值查找</p><p>当前面四种比较都不行的时候,则会去通过key值进行查找。查找时候是当前的新节点,去遍历旧节点数组,找到相同的旧节点,然后将其移到 oldStartVnode 前面。大致流程是:<br><img src="http://doc.xhiteam.com/Public/Uploads/2020-06-07/5edcef3e7cdf5.png"><br>6、处理剩余节点<br>接着就是处理余下的新旧节点。有两种情况:<br>(1)新节处理完了,旧节点还有剩余<br>将剩余的旧节点,逐个删除即可。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">// 删除剩余的旧节点</span><br><span class="line"> removeVnodes(oldCh, oldStartIdx, oldEndIdx)</span><br><span class="line"> </span><br><span class="line"> function removeVnodes(vnodes, startIdx, endIdx) {</span><br><span class="line"> for (; startIdx <= endIdx; ++startIdx) {</span><br><span class="line"> const ch = vnodes[startIdx]</span><br><span class="line"> if (isDef(ch)) {</span><br><span class="line"> if (isDef(ch.tag)) {</span><br><span class="line"> removeAndInvokeRemoveHook(ch)</span><br><span class="line"> invokeDestroyHook(ch)</span><br><span class="line"> } else { // Text node</span><br><span class="line"> removeNode(ch.elm)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>2)新节点有剩余,旧节点处理完了 逐个创建剩余的新节点。有个问题是,将剩余的新节点创建好后,插入到哪里呢?</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">// 将剩余的新节点创建为真实的 DOM 插入</span><br><span class="line"> refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm</span><br><span class="line"> addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)</span><br><span class="line"> function addVnodes(parentElm, refElm, vnodes, startIdx, endIdx, insertedVnodeQueue) {</span><br><span class="line"> for (; startIdx <= endIdx; ++startIdx) {</span><br><span class="line"> createElm(vnodes[startIdx], insertedVnodeQueue, parentElm, refElm, false, vnodes, startIdx)</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>可以看到,refElm 是获取新节点最后一个节点。<br>如果refElm存在的话,说明最后一个节点之前被处理过,那么剩余的新节点插入到refElm前面即可。<br>如果refElm不存在,则将剩余的新节点插入到父节点孩子的末尾。</p><h1 id="4-总结"><a href="#4-总结" class="headerlink" title="4.总结"></a>4.总结</h1><p>diff算法流程图:<br><img src="http://doc.xhiteam.com/Public/Uploads/2020-09-13/5f5e306a34a2f.png"></p>]]></content>
<summary type="html"><p><strong>前言:当数据发生变化时,vue是如何去更新节点的?</strong></p>
<p>渲染真实的DOM开销是很大的,如果当修改了某个数据后直接渲染到真实dom上回引起整个dom数的重绘和重排,下例展示了一个“昂贵的”DOM:<br>控制台输入:</p>
<fi</summary>
<category term="前端组分享" scheme="https://xhiteam.github.io/categories/%E5%89%8D%E7%AB%AF%E7%BB%84%E5%88%86%E4%BA%AB/"/>
<category term="huihui_yin" scheme="https://xhiteam.github.io/tags/huihui-yin/"/>
<category term="Vue算法" scheme="https://xhiteam.github.io/tags/Vue%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>关于xhiteam团队</title>
<link href="https://xhiteam.github.io/2020/09/14/%E5%85%B3%E4%BA%8E%E5%9B%A2%E9%98%9F/"/>
<id>https://xhiteam.github.io/2020/09/14/%E5%85%B3%E4%BA%8E%E5%9B%A2%E9%98%9F/</id>
<published>2020-09-14T12:44:51.000Z</published>
<updated>2020-11-02T12:46:04.518Z</updated>
<content type="html"><![CDATA[<h3 id="Believe-in-Technology"><a href="#Believe-in-Technology" class="headerlink" title="Believe in Technology"></a>Believe in Technology</h3><ul><li>Wechat: LamboChen2306</li><li>Email: <a href="mailto:lambo.chen.2306@gmail.com">lambo.chen.2306@gmail.com</a></li><li>Github: <a href="https://github.com/xhiteam">https://github.com/xhiteam</a></li><li>Wechat Official Account:xhiteam</li></ul>]]></content>
<summary type="html"><h3 id="Believe-in-Technology"><a href="#Believe-in-Technology" class="headerlink" title="Believe in Technology"></a>Believe in Technology</</summary>
<category term="团队" scheme="https://xhiteam.github.io/categories/%E5%9B%A2%E9%98%9F/"/>
<category term="团队介绍" scheme="https://xhiteam.github.io/tags/%E5%9B%A2%E9%98%9F%E4%BB%8B%E7%BB%8D/"/>
</entry>
</feed>