亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

vue.js?diff算法原理詳細(xì)解析

 更新時(shí)間:2022年06月28日 08:23:33   作者:??Dddusty????  
這篇文章主要介紹了vue.js?diff算法原理詳細(xì)解析,diff算法可以看作是一種對(duì)比算法,對(duì)比的對(duì)象是新舊虛擬Dom。顧名思義,diff算法可以找到新舊虛擬Dom之間的差異,但diff算法中其實(shí)并不是只有對(duì)比虛擬Dom,還有根據(jù)對(duì)比后的結(jié)果更新真實(shí)Dom

diff算法的概念

diff算法可以看作是一種對(duì)比算法,對(duì)比的對(duì)象是新舊虛擬Dom。顧名思義,diff算法可以找到新舊虛擬Dom之間的差異,但diff算法中其實(shí)并不是只有對(duì)比虛擬Dom,還有根據(jù)對(duì)比后的結(jié)果更新真實(shí)Dom。

虛擬Dom

上面的概念我們提到了虛擬Dom,相信大家對(duì)這個(gè)名詞并不陌生,下面為大家解釋一下虛擬Dom的概念,以及diff算法中為什么要對(duì)比虛擬Dom,而不是直接去操作真實(shí)Dom。

虛擬Dom,其實(shí)很簡(jiǎn)單,就是一個(gè)用來描述真實(shí)Dom的對(duì)象

它有六個(gè)屬性,sel表示當(dāng)前節(jié)點(diǎn)標(biāo)簽名,data內(nèi)是節(jié)點(diǎn)的屬性,children表示當(dāng)前節(jié)點(diǎn)的其他子標(biāo)簽節(jié)點(diǎn),elm表示當(dāng)前虛擬節(jié)點(diǎn)對(duì)應(yīng)的真實(shí)節(jié)點(diǎn)(這里暫時(shí)沒有),key即為當(dāng)前節(jié)點(diǎn)的key,text表示當(dāng)前節(jié)點(diǎn)下的文本,結(jié)構(gòu)類似這樣。

let vnode = {
    sel: 'ul', 
 ? ?data: {},
    children: [ 
        {
            sel: 'li', data: { class: 'item' }, text: 'son1'
        },
        {
            sel: 'li', data: { class: 'item' }, text: 'son2'
        }, ? ?
 ?  ],
 ? ?elm: undefined,
 ? ?key: undefined,
 ? ?text: undefined
}

那么虛擬Dom有什么用呢。我們其實(shí)可以把虛擬Dom理解成對(duì)應(yīng)真實(shí)Dom的一種狀態(tài)。當(dāng)真實(shí)Dom發(fā)生變化后,虛擬Dom可以為我們提供這個(gè)真實(shí)Dom變化之前和變化之后的狀態(tài),我們通過對(duì)比這兩個(gè)狀態(tài),即可得出真實(shí)Dom真正需要更新的部分,即可實(shí)現(xiàn)最小量更新。在一些比較復(fù)雜的Dom變化場(chǎng)景中,通過對(duì)比虛擬Dom后更新真實(shí)Dom會(huì)比直接更新真實(shí)Dom的效率高,這也就是虛擬Dom和diff算法真正存在的意義。

h函數(shù)

在介紹diff算法原理之前還需要簡(jiǎn)單讓大家了解一下h函數(shù),因?yàn)槲覀円克鼮槲覀兩商摂MDom。這個(gè)h函數(shù)大家應(yīng)該也比較熟悉,就是render函數(shù)里面?zhèn)魅氲哪莻€(gè)h函數(shù)。

h函數(shù)可以接受多種類型的參數(shù),但其實(shí)它內(nèi)部只干了一件事,就是執(zhí)行vnode函數(shù)。根據(jù)傳入h函數(shù)的參數(shù)來決定執(zhí)行vnode函數(shù)時(shí)傳入的參數(shù)。那么vnode函數(shù)又是干什么的呢?vnode函數(shù)其實(shí)也只干了一件事,就是把傳入h函數(shù)的參數(shù)轉(zhuǎn)化為一個(gè)對(duì)象,即虛擬Dom。

// vnode.js
export default function (sel, data, children, text, elm) {
    const key = data.key 
    return {sel, data, children, text, elm, key}
}

執(zhí)行h函數(shù)后,內(nèi)部會(huì)通過vnode函數(shù)生成虛擬Dom,h函數(shù)把這個(gè)虛擬在return出去。

diff對(duì)比規(guī)則

明確了h函數(shù)是干什么的,我們可以簡(jiǎn)單用h函數(shù)生成兩個(gè)不同的虛擬節(jié)點(diǎn),我們將通過一個(gè)簡(jiǎn)易版的diff算法代碼介紹diff對(duì)比的具體流程。

// 第一個(gè)參數(shù)是sel 第二個(gè)參數(shù)是data 第三個(gè)參數(shù)是children
const myVnode1 = h("h1", {}, [
 ?h("p", {key: "a"}, "a"),
 ?h("p", {key: "b"}, "b"),
]);
const myVnode2 = h("h1", {}, [
 ?h("p", {key: "c"}, "c"),
 ?h("p", {key: "d"}, "d"),
]);

patch

比較的第一步就是執(zhí)行patch,它相當(dāng)于對(duì)比的入口。既然是對(duì)比兩個(gè)虛擬Dom,那么就將兩個(gè)虛擬Dom作為參數(shù)傳入patch中。patch的主要作用是對(duì)比兩個(gè)虛擬Dom的根節(jié)點(diǎn),并根據(jù)對(duì)比結(jié)果操作真實(shí)Dom。

patch函數(shù)的核心代碼如下,注意注釋。

// patch.js
import vnode from "./vnode"
import patchDetails from "./patchVnode"
import createEle from "./createEle"
/**
 * @description 用來對(duì)比兩個(gè)虛擬dom的根節(jié)點(diǎn),并根據(jù)對(duì)比結(jié)果操作真實(shí)Dom
 * @param {*} oldVnode 
 * @param {*} newVnode 
 */
export function patch(oldVnode, newVnode) {
 ?// 1.判斷oldVnode是否為虛擬節(jié)點(diǎn),不是的話轉(zhuǎn)化為虛擬節(jié)點(diǎn)
 ?if(!oldVnode.sel) {
 ? ?// 轉(zhuǎn)化為虛擬節(jié)點(diǎn)
 ? ?oldVnode = vnode(oldVnode.tagName.toLowerCase(), {}, [], undefined, oldVnode)
  }
 ?// 2.判斷oldVnode和newVnode是否為同一個(gè)節(jié)點(diǎn)
 ?if(oldVnode.key == newVnode.key && oldVnode.sel == newVnode.sel) {
 ? ?console.log('是同一個(gè)節(jié)點(diǎn)')
 ? ?// 比較子節(jié)點(diǎn)
 ? ?patchDetails(oldVnode, newVnode)
  }else {
 ? ?console.log('不是同一個(gè)節(jié)點(diǎn)')
 ? ?// 插入newVnode 
 ? ?const newNode = createEle(newVnode) // 插入之前需要先將newVnode轉(zhuǎn)化為dom
 ? ?oldVnode.elm.parentNode.insertBefore(newNode, oldVnode.elm) // 插入操作
 ? ?// 刪除oldVnode
 ? ?oldVnode.elm.parentNode.removeChild(oldVnode.elm)
  }
}
// createEle.js
/**
 * @description 根據(jù)傳入的虛擬Dom生成真實(shí)Dom
 * @param {*} vnode 
 * @returns real node
 */
export default function createEle (vnode) {
 ?const realNode = document.createElement(vnode.sel)
?
 ?// 子節(jié)點(diǎn)轉(zhuǎn)換
 ?if(vnode.text && (vnode.children == undefined || (vnode.children && vnode.children.length == 0)) ) {
 ? ?// 子節(jié)點(diǎn)只含有文本
 ? ?realNode.innerText = vnode.text ?
  }else if(Array.isArray(vnode.children) && vnode.children.length > 0) {
 ? ?// 子節(jié)點(diǎn)為其他虛擬節(jié)點(diǎn) 遞歸添加node
 ? ?for(let i = 0; i < vnode.children.length; i++) {
 ? ? ?const childNode = createEle(vnode.children[i])
 ? ? ?realNode.appendChild(childNode)
 ?  }
  }
 ?// 補(bǔ)充vnode的elm屬性
 ?vnode.elm = realNode
 ?return vnode.elm
}

patchVnode

patchVnode用來比較兩個(gè)虛擬節(jié)點(diǎn)的子節(jié)點(diǎn)并更新其子節(jié)點(diǎn)對(duì)應(yīng)的真實(shí)Dom節(jié)點(diǎn)

// patchVnode.js
?
import updateChildren from "./updateChildren"
import createEle from "./createEle"
?
/**
 * @description 比較兩個(gè)虛擬節(jié)點(diǎn)的子節(jié)點(diǎn)(children or text) 并更新其子節(jié)點(diǎn)對(duì)應(yīng)的真實(shí)dom節(jié)點(diǎn)
 * @param {*} oldVnode 
 * @param {*} newVnode 
 * @returns 
 */
export function patchDetails(oldVnode, newVnode) {
 ?// 判斷oldVnode和newVnode是否為同一個(gè)對(duì)象, 是的話直接不用比了
 ?if(oldVnode == newVnode) return 
?
 ?// 默認(rèn)newVnode和oldVnode只有text和children其中之一,真實(shí)的源碼這里的情況會(huì)更多一些,不過大同小異。
?
 ?if(hasText(newVnode)) {
 ? ?// newVnode有text但沒有children
?
 ? ?/**
 ? ? *  newVnode.text !== oldVnode.text 直接囊括了兩種情況
 ? ? *  1.oldVnode有text無children 但是text和newVnode的text內(nèi)容不同
 ? ? *  2.oldVnode無text有children 此時(shí)oldVnode.text為undefined 
 ? ? *  兩種情況都可以通過innerText屬性直接完成dom更新 
 ? ? *  情況1直接更新text 情況2相當(dāng)于去掉了children后加了新的text
 ? ? */
 ? ?if(newVnode.text !== oldVnode.text) {
 ? ? ?oldVnode.elm.innerText = newVnode.text
 ?  }
  }else if(hasChildren(newVnode)) {
 ? ?// newVnode有children但是沒有text
 ? ?
 ? ?if(hasText(oldVnode)) {
 ? ? ?// oldVnode有text但是沒有children
 ? ? ?
 ? ? ?oldVnode.elm.innerText = '' // 刪除oldVnode的text
 ? ? ?// 添加newVnode的children
 ? ? ?for(let i = 0; i < newVnode.children.length; i++) {
 ? ? ? ?oldVnode.elm.appendChild(createEle(newVnode.children[i]))
 ? ?  }
 ?  }else if(hasChildren(oldVnode)) {
 ? ? ?// oldVnode有children但是沒有text
?
 ? ? ?// 對(duì)比兩個(gè)節(jié)點(diǎn)的children 并更新對(duì)應(yīng)的真實(shí)dom節(jié)點(diǎn)
 ? ? ?updateChildren(oldVnode.children, newVnode.children, oldVnode.elm)
 ?  }
  }
}
// 有children沒有text
function hasChildren(node) {
 ?return !node.text && (node.children && node.children.length > 0)
} 
// 有text沒有children
function hasText(node) {
 ?return node.text && (node.children == undefined || (node.children && node.children.length == 0))
} 

updateChildren

該方法是diff算法中最復(fù)雜的方法(大的要來了)。對(duì)應(yīng)上面patchVnodeoldVnodenewVnode都有children的情況。

首先我們需要介紹一下這里的對(duì)比規(guī)則。

對(duì)比過程中會(huì)引入四個(gè)指針,分別指向oldVnode子節(jié)點(diǎn)列表中的第一個(gè)節(jié)點(diǎn)和最后一個(gè)節(jié)點(diǎn)(后面我們簡(jiǎn)稱為舊前舊后)以及指向newVnode子節(jié)點(diǎn)列表中的第一個(gè)節(jié)點(diǎn)和最后一個(gè)節(jié)點(diǎn)(后面我們簡(jiǎn)稱為新前新后

對(duì)比時(shí),每一次對(duì)比按照以下順序進(jìn)行命中查找

  • 舊前與新前節(jié)點(diǎn)對(duì)比(1)
  • 舊后與新后節(jié)點(diǎn)對(duì)比(2)
  • 舊前與新后節(jié)點(diǎn)對(duì)比(3)
  • 舊后與新前節(jié)點(diǎn)對(duì)比(4)

上述四種情況,如果某一種情況兩個(gè)指針對(duì)應(yīng)的虛擬Dom相同,那么我們稱之為命中。命中后就不會(huì)接著查找了,指針會(huì)移動(dòng),(還有可能會(huì)操作真實(shí)Dom,3或者4命中時(shí)會(huì)操作真實(shí)Dom移動(dòng)節(jié)點(diǎn))之后開始下一次對(duì)比。如果都沒有命中,則去oldVnode子節(jié)點(diǎn)列表循環(huán)查找當(dāng)前新前指針?biāo)赶虻墓?jié)點(diǎn),如果查到了,那么操作真實(shí)Dom移動(dòng)節(jié)點(diǎn),沒查到則新增真實(shí)Dom節(jié)點(diǎn)插入。

這種模式的對(duì)比會(huì)一直進(jìn)行,直到滿足了終止條件。即舊前指針移動(dòng)到了舊后指針的后面或者新前指針移動(dòng)到了新后指針的后面,我們可以理解為舊子節(jié)點(diǎn)先處理完畢新子節(jié)點(diǎn)處理完畢。那么我們可以預(yù)想到新舊子節(jié)點(diǎn)中總會(huì)有其一先處理完,對(duì)比結(jié)束后,我們會(huì)根據(jù)沒有處理完子節(jié)點(diǎn)的那一對(duì)前后指針決定是要插入真實(shí)Dom還是刪除真實(shí)Dom。

  • 如果舊子節(jié)點(diǎn)先處理完了,新子節(jié)點(diǎn)有剩余,說明有要新增的節(jié)點(diǎn)。將根據(jù)最終新前新后之間的虛擬節(jié)點(diǎn)執(zhí)行插入操作
  • 如果新子節(jié)點(diǎn)先處理完了,舊子節(jié)點(diǎn)有剩余,說明有要?jiǎng)h除的節(jié)點(diǎn)。將根據(jù)最終舊前舊后之間的虛擬節(jié)點(diǎn)執(zhí)行刪除操作

下面將呈現(xiàn)代碼,注意注釋:

// updateChildren.js
import patchDetails from "./patchVnode"
import createEle from "./createEle";
/**
 * @description 對(duì)比子節(jié)點(diǎn)列表并更新真實(shí)Dom
 * @param {*} oldCh 舊虛擬Dom子節(jié)點(diǎn)列表 
 * @param {*} newCh 新虛擬Dom子節(jié)點(diǎn)列表 
 * @param {*} parent 新舊虛擬節(jié)點(diǎn)對(duì)應(yīng)的真實(shí)Dom
 * @returns 
 */
export default function updateChildren(oldCh, newCh, parent) {
 ?// 定義四個(gè)指針 舊前 舊后 新前 新后 (四個(gè)指針兩兩一對(duì),每一對(duì)前后指針?biāo)赶虻墓?jié)點(diǎn)以及其之間的節(jié)點(diǎn)為未處理的子節(jié)點(diǎn))
 ?let oldStartIndex = 0;
 ?let oldEndIndex = oldCh.length - 1;
 ?let newStartIndex = 0;
 ?let newEndIndex = newCh.length - 1;
?
 ?// 四個(gè)指針對(duì)應(yīng)的節(jié)點(diǎn)
 ?let oldStartNode = oldCh[oldStartIndex];
 ?let oldEndNode = oldCh[oldEndIndex];
 ?let newStartNode = newCh[newStartIndex];
 ?let newEndNode = newCh[newEndIndex];
?
 ?// oldCh中每個(gè)子節(jié)點(diǎn) key 與 index的哈希表 用于四種對(duì)比規(guī)則都不匹配的情況下在oldCh中尋找節(jié)點(diǎn)
 ?const keyMap = new Map();
?
 ?/**
 ? * 開始遍歷兩個(gè)children數(shù)組進(jìn)行細(xì)節(jié)對(duì)比
 ? * 對(duì)比規(guī)則:舊前-新前 舊后-新后 舊前-新后 舊后-新前
 ? * 對(duì)比之后指針進(jìn)行移動(dòng)
 ? * 直到指針不滿足以下條件 意味著有一對(duì)前后指針之間再無未處理的子節(jié)點(diǎn) 則停止對(duì)比 直接操作DOM
 ? */
 ?while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
 ? ?// 這四種情況是為了讓指針在移動(dòng)的過程中跳過空節(jié)點(diǎn)
 ? ?if (oldStartNode == undefined) {
 ? ? ?oldStartNode = oldCh[++oldStartIndex];
 ?  } else if (oldEndNode == undefined) {
 ? ? ?oldEndNode = oldCh[--oldEndIndex];
 ?  } else if (newStartNode == undefined) {
 ? ? ?newStartNode = newCh[++newStartIndex];
 ?  } else if (newEndNode == undefined) {
 ? ? ?newEndNode = newCh[--newEndIndex];
 ?  } else if (isSame(oldStartNode, newStartNode)) {
 ? ? ?console.log("method1");
 ? ? ?// 舊前-新前是同一個(gè)虛擬節(jié)點(diǎn)
?
 ? ? ?// 兩個(gè)子節(jié)點(diǎn)再對(duì)比他們的子節(jié)點(diǎn)并更新dom (遞歸切入點(diǎn))
 ? ? ?patchDetails(oldStartNode, newStartNode);
 ? ? ?// 指針移動(dòng)
 ? ? ?oldStartNode = oldCh[++oldStartIndex];
 ? ? ?newStartNode = newCh[++newStartIndex];
 ?  } else if (isSame(oldEndNode, newEndNode)) {
 ? ? ?console.log("method2");
 ? ? ?// 舊后-新后是同一個(gè)虛擬節(jié)點(diǎn)
?
 ? ? ?// 兩個(gè)子節(jié)點(diǎn)再對(duì)比他們的子節(jié)點(diǎn)并更新dom (遞歸切入點(diǎn))
 ? ? ?patchDetails(oldEndNode, newEndNode);
 ? ? ?// 指針移動(dòng)
 ? ? ?oldEndNode = oldCh[--oldEndIndex];
 ? ? ?newEndNode = newCh[--newEndIndex];
 ?  } else if (isSame(oldStartNode, newEndNode)) {
 ? ? ?console.log("method3");
 ? ? ?// 舊前-新后是同一個(gè)虛擬節(jié)點(diǎn)
?
 ? ? ?// 兩個(gè)子節(jié)點(diǎn)再對(duì)比他們的子節(jié)點(diǎn)并更新dom (遞歸切入點(diǎn))
 ? ? ?patchDetails(oldStartNode, newEndNode);
?
 ? ? ?/**
 ? ? ? *  這一步多一個(gè)移動(dòng)(真實(shí))節(jié)點(diǎn)的操作
 ? ? ? *  需要把當(dāng)前指針?biāo)赶虻淖庸?jié)點(diǎn) 移動(dòng)到 oldEndIndex所對(duì)應(yīng)真實(shí)節(jié)點(diǎn)之后(也就是未處理真實(shí)節(jié)點(diǎn)的尾部)
 ? ? ? *  注意:這一步是在操作真實(shí)節(jié)點(diǎn)
 ? ? ? */
 ? ? ?parent.insertBefore(oldStartNode.elm, oldEndNode.elm.nextSibling);
?
 ? ? ?// 指針移動(dòng)
 ? ? ?oldStartNode = oldCh[++oldStartIndex];
 ? ? ?newEndNode = newCh[--newEndIndex];
 ?  } else if (isSame(oldEndNode, newStartNode)) {
 ? ? ?console.log("method4");
 ? ? ?// 舊后-新前 是同一個(gè)虛擬節(jié)點(diǎn)
?
 ? ? ?// 兩個(gè)子節(jié)點(diǎn)再對(duì)比他們的子節(jié)點(diǎn)并更新dom (遞歸切入點(diǎn))
 ? ? ?patchDetails(oldEndNode, newStartNode);
 ? ? ?/**
 ? ? ? *  這一步多一個(gè)移動(dòng)(真實(shí))節(jié)點(diǎn)的操作
 ? ? ? *  與method3不同在移動(dòng)位置
 ? ? ? *  需要把當(dāng)前指針?biāo)赶虻淖庸?jié)點(diǎn) 移動(dòng)到 oldStartIndex所對(duì)應(yīng)真實(shí)節(jié)點(diǎn)之前(也就是未處理真實(shí)節(jié)點(diǎn)的頂部)
 ? ? ? *  注意:這一步是在操作真實(shí)節(jié)點(diǎn)
 ? ? ? */
 ? ? ?parent.insertBefore(oldEndNode.elm, oldCh[oldStartIndex].elm);
?
 ? ? ?// 指針移動(dòng)
 ? ? ?oldEndNode = oldCh[--oldEndIndex];
 ? ? ?newStartNode = newCh[++newStartIndex];
 ?  } else {
 ? ? ?console.log("does not match");
 ? ? ?// 四種規(guī)則都不匹配
?
 ? ? ?// 生成keyMap
 ? ? ?if (keyMap.size == 0) {
 ? ? ? ?for (let i = oldStartIndex; i <= oldEndIndex; i++) {
 ? ? ? ? ?if (oldCh[i].key) keyMap.set(oldCh[i].key, i);
 ? ? ?  }
 ? ?  }
?
 ? ? ?// 在oldCh中搜索當(dāng)前newStartIndex所指向的節(jié)點(diǎn)
 ? ? ?if (keyMap.has(newStartNode.key)) {
 ? ? ? ?// 搜索到了
?
 ? ? ? ?// 先獲取oldCh中該虛擬節(jié)點(diǎn)
 ? ? ? ?const oldMoveNode = oldCh[keyMap.get(newStartNode.key)];
 ? ? ? ?// 兩個(gè)子節(jié)點(diǎn)再對(duì)比他們的子節(jié)點(diǎn)并更新dom (遞歸切入點(diǎn))
 ? ? ? ?patchDetails(oldMoveNode, newStartNode);
?
 ? ? ? ?// 移動(dòng)這個(gè)節(jié)點(diǎn)(移動(dòng)的是真實(shí)節(jié)點(diǎn))
 ? ? ? ?parent.insertBefore(oldMoveNode.elm, oldStartNode.elm);
?
 ? ? ? ?// 該虛擬節(jié)點(diǎn)設(shè)置為undefined(還記得最開始的四個(gè)條件嗎,因?yàn)檫@里會(huì)將子節(jié)點(diǎn)制空,所以加了那四個(gè)條件)
 ? ? ? ?oldCh[keyMap.get(newStartNode.key)] = undefined;
 ? ? ? ? ?
 ? ?  } else {
 ? ? ? ?// 沒搜索到 直接插入
 ? ? ? ?parent.insertBefore(createEle(newStartNode), oldStartNode.elm);
 ? ?  }
?
 ? ? ?// 指針移動(dòng)
 ? ? ?newStartNode = newCh[++newStartIndex];
 ?  }
  }
?
 ?/**
 ? * 插入和刪除節(jié)點(diǎn)
 ? * while結(jié)束后 有一對(duì)前后指針之間仍然有未處理的子節(jié)點(diǎn),那么就會(huì)進(jìn)行插入或者刪除操作
 ? * oldCh的雙指針中有未處理的子節(jié)點(diǎn),進(jìn)行刪除操作
 ? * newCh的雙指針中有未處理的子節(jié)點(diǎn),進(jìn)行插入操作
 ? */
 ?if (oldStartIndex <= oldEndIndex) {
 ? ?// 刪除
 ? ?for (let i = oldStartIndex; i <= oldEndIndex; i++) {
 ? ? ?// 加判斷是因?yàn)閛ldCh[i]有可能為undefined
 ? ? ?if(oldCh[i]) parent.removeChild(oldCh[i].elm);
 ?  }
  } else if (newStartIndex <= newEndIndex) {
 ? ?/**
 ? ? * 插入
 ? ? * 這里需要注意的點(diǎn)是從哪里插入,也就是appendChild的第二個(gè)參數(shù)
 ? ? * 應(yīng)該從oldStartIndex對(duì)應(yīng)的位置插入
 ? ? */
 ? ?for (let i = newStartIndex; i <= newEndIndex; i++) {
 ? ? ?// oldCh[oldStartIndex]存在是從頭部插入
 ? ? ?parent.insertBefore(createEle(newCh[i]), oldCh[oldStartIndex] ? oldCh[oldStartIndex].elm : undefined);
 ?  }
  }
}
?
// 判斷兩個(gè)虛擬節(jié)點(diǎn)是否為同一個(gè)虛擬節(jié)點(diǎn)
function isSame(a, b) {
 ?return a.sel == b.sel && a.key == b.key;
}

這里的邏輯稍微比較復(fù)雜,需要大家多理幾遍,必要的話,自己手畫一張圖自己移動(dòng)一下指針。著重需要注意的地方是操作真實(shí)Dom時(shí),插入、移動(dòng)節(jié)點(diǎn)應(yīng)該將節(jié)點(diǎn)從哪里插入或者移動(dòng)到哪里,其實(shí)基本插入到oldStartIndex對(duì)應(yīng)的真實(shí)Dom的前面,除了第三種命中后的移動(dòng)節(jié)點(diǎn)操作,是移動(dòng)到oldEndIndex所對(duì)應(yīng)真實(shí)節(jié)點(diǎn)之后

總結(jié)

由于diff算法對(duì)比的是虛擬Dom,而虛擬Dom是呈樹狀的,所以我們可以發(fā)現(xiàn),diff算法中充滿了遞歸??偨Y(jié)起來,其實(shí)diff算法就是一個(gè) patch —> patchVnode —> updateChildren —> patchVnode —> updateChildren —> patchVnode這樣的一個(gè)循環(huán)遞歸的過程。

這里再提一嘴key,我們面試中經(jīng)常會(huì)被問到vue中key的作用。根據(jù)上面我們分析的,key的主要作用其實(shí)就是對(duì)比兩個(gè)虛擬節(jié)點(diǎn)時(shí),判斷其是否為相同節(jié)點(diǎn)。加了key以后,我們可以更為明確的判斷兩個(gè)節(jié)點(diǎn)是否為同一個(gè)虛擬節(jié)點(diǎn),是的話判斷子節(jié)點(diǎn)是否有變更(有變更更新真實(shí)Dom),不是的話繼續(xù)比。如果不加key的話,如果兩個(gè)不同節(jié)點(diǎn)的標(biāo)簽名恰好相同,那么就會(huì)被判定為同一個(gè)節(jié)點(diǎn)(key都為undefined),結(jié)果一對(duì)比這兩個(gè)節(jié)點(diǎn)的子節(jié)點(diǎn)發(fā)現(xiàn)不一樣,這樣會(huì)憑空增加很多對(duì)真實(shí)Dom的操作,從而導(dǎo)致頁面更頻繁地重繪和回流。

所以我認(rèn)為合理利用key可以有效減少真實(shí)Dom的變動(dòng),從而減少頁面重繪和回流的頻率,進(jìn)而提高頁面更新的效率。

到此這篇關(guān)于vue.js diff算法原理詳細(xì)解析的文章就介紹到這了,更多相關(guān)vue.js diff算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue中多路由表頭吸頂實(shí)現(xiàn)的幾種布局方式

    vue中多路由表頭吸頂實(shí)現(xiàn)的幾種布局方式

    這篇文章主要介紹了vue項(xiàng)目多路由表頭吸頂實(shí)現(xiàn)的幾種布局方式,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-04-04
  • 詳解Vue中的keep-alive

    詳解Vue中的keep-alive

    這篇文章主要為大家介紹了Vue中的keep-alive,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • 詳解vue中$router和$route的區(qū)別

    詳解vue中$router和$route的區(qū)別

    這篇文章主要介紹了vue中$router和$route的區(qū)別,對(duì)vue感興趣的同學(xué),一定要看下
    2021-05-05
  • vue實(shí)現(xiàn)放大鏡效果

    vue實(shí)現(xiàn)放大鏡效果

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)放大鏡效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-09-09
  • 利用Vue實(shí)現(xiàn)卡牌翻轉(zhuǎn)的特效

    利用Vue實(shí)現(xiàn)卡牌翻轉(zhuǎn)的特效

    這篇文章主要介紹了如何利用Vue實(shí)現(xiàn)一個(gè)春節(jié)抽??撁?,采用了卡牌翻轉(zhuǎn)的形式。文中的實(shí)現(xiàn)方法講解詳細(xì),快跟隨小編一起學(xué)習(xí)一下吧
    2022-02-02
  • checkbox在vue中的用法小結(jié)

    checkbox在vue中的用法小結(jié)

    之前對(duì)于vue中用到過的checkbox也只是別人寫好的組件,這次在自己實(shí)現(xiàn)時(shí)走了很多坑,特意寫這篇文章記錄到腳本之家平臺(tái),供大家參考
    2018-11-11
  • vue實(shí)現(xiàn)豎屏滾動(dòng)公告效果

    vue實(shí)現(xiàn)豎屏滾動(dòng)公告效果

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)豎屏滾動(dòng)公告效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Vue.js每天必學(xué)之Class與樣式綁定

    Vue.js每天必學(xué)之Class與樣式綁定

    這篇文章主要為大家詳細(xì)介紹了Vue.js的Class與樣式綁定,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • element-ui下拉菜單組件Dropdown的示例代碼

    element-ui下拉菜單組件Dropdown的示例代碼

    這篇文章主要介紹了element-ui下拉菜單組件Dropdown,本文詳細(xì)給大家介紹了我遇到的一個(gè)最大的坑,通過結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • Vue3 插槽使用匯總

    Vue3 插槽使用匯總

    這篇文章主要給大家分享了Vue3的 插槽使用匯總,在 2.6.0中,vue 為具名插槽和作用于插槽引入了一個(gè)新的統(tǒng)一語法:v-slot。它取代了 slot 和 slot-scope 在新版中的應(yīng)用,下面就一起來看看文章的詳細(xì)內(nèi)容吧
    2021-12-12

最新評(píng)論