最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一篇文章帶你搞懂Vue虛擬Dom與diff算法

 更新時(shí)間:2020年08月25日 17:11:59   作者:是蹄蹄吖  
這篇文章主要給大家介紹了關(guān)于Vue虛擬Dom與diff算法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

使用過Vue和React的小伙伴肯定對(duì)虛擬Dom和diff算法很熟悉,它扮演著很重要的角色。由于小編接觸Vue比較多,React只是淺學(xué),所以本篇主要針對(duì)Vue來展開介紹,帶你一步一步搞懂它。

虛擬DOM

什么是虛擬DOM?

虛擬DOM(Virtual   Dom),也就是我們常說的虛擬節(jié)點(diǎn),是用JS對(duì)象來模擬真實(shí)DOM中的節(jié)點(diǎn),該對(duì)象包含了真實(shí)DOM的結(jié)構(gòu)及其屬性,用于對(duì)比虛擬DOM和真實(shí)DOM的差異,從而進(jìn)行局部渲染來達(dá)到優(yōu)化性能的目的。
真實(shí)的元素節(jié)點(diǎn):

<div id="wrap">
 <p class="title">Hello world!</p>
</div>

VNode:

{
 tag:'div',
 attrs:{
 id:'wrap'
 },
 children:[
 {
  tag:'p',
  text:'Hello world!',
  attrs:{
  class:'title',
  }
 }
 ]
}

為什么使用虛擬DOM?

簡單了解虛擬DOM后,是不是有小伙伴會(huì)問:Vue和React框架中為什么會(huì)用到它呢?好問題!那來解決下小伙伴的疑問。
起初我們?cè)谑褂肑S/JQuery時(shí),不可避免的會(huì)大量操作DOM,而DOM的變化又會(huì)引發(fā)回流或重繪,從而降低頁面渲染性能。那么怎樣來減少對(duì)DOM的操作呢?此時(shí)虛擬DOM應(yīng)用而生,所以虛擬DOM出現(xiàn)的主要目的就是為了減少頻繁操作DOM而引起回流重繪所引發(fā)的性能問題的!

虛擬DOM的作用是什么?

  1. 兼容性好。因?yàn)閂node本質(zhì)是JS對(duì)象,所以不管Node還是瀏覽器環(huán)境,都可以操作;
  2. 減少了對(duì)Dom的操作。頁面中的數(shù)據(jù)和狀態(tài)變化,都通過Vnode對(duì)比,只需要在比對(duì)完之后更新DOM,不需要頻繁操作,提高了頁面性能;

虛擬DOM和真實(shí)DOM的區(qū)別?

說到這里,那么虛擬DOM和真實(shí)DOM的區(qū)別是什么呢?總結(jié)大概如下:

  • 虛擬DOM不會(huì)進(jìn)行回流和重繪;
  • 真實(shí)DOM在頻繁操作時(shí)引發(fā)的回流重繪導(dǎo)致性能很低;
  • 虛擬DOM頻繁修改,然后一次性對(duì)比差異并修改真實(shí)DOM,最后進(jìn)行依次回流重繪,減少了真實(shí)DOM中多次回流重繪引起的性能損耗;
  • 虛擬DOM有效降低大面積的重繪與排版,因?yàn)槭呛驼鎸?shí)DOM對(duì)比,更新差異部分,所以只渲染局部;

總損耗 = 真實(shí)DOM增刪改 + (多節(jié)點(diǎn))回流/重繪;    //計(jì)算使用真實(shí)DOM的損耗
總損耗 = 虛擬DOM增刪改 + (diff對(duì)比)真實(shí)DOM差異化增刪改 + (較少節(jié)點(diǎn))回流/重繪;   //計(jì)算使用虛擬DOM的損耗

可以發(fā)現(xiàn),都是圍繞頻繁操作真實(shí)DOM引起回流重繪,導(dǎo)致頁面性能損耗來說的。不過框架也不一定非要使用虛擬DOM,關(guān)鍵在于看是否頻繁操作會(huì)引起大面積的DOM操作。

那么虛擬DOM究竟通過什么方式來減少了頁面中頻繁操作DOM呢?這就不得不去了解DOM Diff算法了。

DIFF算法

當(dāng)數(shù)據(jù)變化時(shí),vue如何來更新視圖的?其實(shí)很簡單,一開始會(huì)根據(jù)真實(shí)DOM生成虛擬DOM,當(dāng)虛擬DOM某個(gè)節(jié)點(diǎn)的數(shù)據(jù)改變后會(huì)生成一個(gè)新的Vnode,然后VNode和oldVnode對(duì)比,把不同的地方修改在真實(shí)DOM上,最后再使得oldVnode的值為Vnode。

diff過程就是調(diào)用patch函數(shù),比較新老節(jié)點(diǎn),一邊比較一邊給真實(shí)DOM打補(bǔ)丁(patch);

對(duì)照vue源碼來解析一下,貼出核心代碼,旨在簡單明了講述清楚,不然小編自己看著都頭大了O(∩_∩)O

patch

那么patch是怎樣打補(bǔ)丁的?

//patch函數(shù) oldVnode:老節(jié)點(diǎn) vnode:新節(jié)點(diǎn)
function patch (oldVnode, vnode) {
 ...
 if (sameVnode(oldVnode, vnode)) {
 patchVnode(oldVnode, vnode) //如果新老節(jié)點(diǎn)是同一節(jié)點(diǎn),那么進(jìn)一步通過patchVnode來比較子節(jié)點(diǎn)
 } else {
 /* -----否則新節(jié)點(diǎn)直接替換老節(jié)點(diǎn)----- */
 const oEl = oldVnode.el // 當(dāng)前oldVnode對(duì)應(yīng)的真實(shí)元素節(jié)點(diǎn)
 let parentEle = api.parentNode(oEl) // 父元素
 createEle(vnode) // 根據(jù)Vnode生成新元素
 if (parentEle !== null) {
  api.insertBefore(parentEle, vnode.el, api.nextSibling(oEl)) // 將新元素添加進(jìn)父元素
  api.removeChild(parentEle, oldVnode.el) // 移除以前的舊元素節(jié)點(diǎn)
  oldVnode = null
 }
 }
 ...
 return vnode
}

//判斷兩節(jié)點(diǎn)是否為同一節(jié)點(diǎn)
function sameVnode (a, b) {
 return (
 a.key === b.key && // key值
 a.tag === b.tag && // 標(biāo)簽名
 a.isComment === b.isComment && // 是否為注釋節(jié)點(diǎn)
 // 是否都定義了data,data包含一些具體信息,例如onclick , style
 isDef(a.data) === isDef(b.data) && 
 sameInputType(a, b) // 當(dāng)標(biāo)簽是<input>的時(shí)候,type必須相同
 )
}

從上面可以看出,patch函數(shù)是通過判斷新老節(jié)點(diǎn)是否為同一節(jié)點(diǎn):

  • 如果是同一節(jié)點(diǎn),執(zhí)行patchVnode進(jìn)行子節(jié)點(diǎn)比較;
  • 如果不是同一節(jié)點(diǎn),新節(jié)點(diǎn)直接替換老節(jié)點(diǎn);

那如果不是同一節(jié)點(diǎn),但是它們子節(jié)點(diǎn)一樣怎么辦嘞?OMG,要牢記:diff是同層比較,不存在跨級(jí)比較的!簡單提一嘴,React中也是如此,它們只是針對(duì)同一層的節(jié)點(diǎn)進(jìn)行比較。

patchVnode

既然到了patchVnode方法,說明新老節(jié)點(diǎn)為同一節(jié)點(diǎn),那么這個(gè)方法做了什么處理?

function patchVnode (oldVnode, vnode) {
 const el = vnode.el = oldVnode.el  //找到對(duì)應(yīng)的真實(shí)DOM
 let i, oldCh = oldVnode.children, ch = vnode.children 
 if (oldVnode === vnode) return  //如果新老節(jié)點(diǎn)相同,直接返回
 if (oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text) {
 //如果新老節(jié)點(diǎn)都有文本節(jié)點(diǎn)且不相等,那么新節(jié)點(diǎn)的文本節(jié)點(diǎn)替換老節(jié)點(diǎn)的文本節(jié)點(diǎn)
 api.setTextContent(el, vnode.text) 
 }else {
 updateEle(el, vnode, oldVnode)
 if (oldCh && ch && oldCh !== ch) {
  //如果新老節(jié)點(diǎn)都有子節(jié)點(diǎn),執(zhí)行updateChildren比較子節(jié)點(diǎn)[很重要也很復(fù)雜,下面展開介紹]
  updateChildren(el, oldCh, ch)
 }else if (ch){
  //如果新節(jié)點(diǎn)有子節(jié)點(diǎn)而老節(jié)點(diǎn)沒有子節(jié)點(diǎn),那么將新節(jié)點(diǎn)的子節(jié)點(diǎn)添加到老節(jié)點(diǎn)上
  createEle(vnode)
 }else if (oldCh){
  //如果新節(jié)點(diǎn)沒有子節(jié)點(diǎn)而老節(jié)點(diǎn)有子節(jié)點(diǎn),那么刪除老節(jié)點(diǎn)的子節(jié)點(diǎn)
  api.removeChildren(el)
 }
 }
}

如果兩個(gè)節(jié)點(diǎn)不一樣,直接用新節(jié)點(diǎn)替換老節(jié)點(diǎn);

如果兩個(gè)節(jié)點(diǎn)一樣,

  • ​新老節(jié)點(diǎn)一樣,直接返回;
  • ​老節(jié)點(diǎn)有子節(jié)點(diǎn),新節(jié)點(diǎn)沒有:刪除老節(jié)點(diǎn)的子節(jié)點(diǎn);
  • 老節(jié)點(diǎn)沒有子節(jié)點(diǎn),新節(jié)點(diǎn)有子節(jié)點(diǎn):新節(jié)點(diǎn)的子節(jié)點(diǎn)直接append到老節(jié)點(diǎn);
  • ​都只有文本節(jié)點(diǎn):直接用新節(jié)點(diǎn)的文本節(jié)點(diǎn)替換老的文本節(jié)點(diǎn);
  • ​都有子節(jié)點(diǎn):updateChildren

最復(fù)雜的情況也就是新老節(jié)點(diǎn)都有子節(jié)點(diǎn),那么updateChildren是如何來處理這一問題的,該方法也是diff算法的核心,下面我們來了解一下!

updateChildren

由于代碼太多了,這里先做個(gè)概述。updateChildren方法的核心:

  1. 提取出新老節(jié)點(diǎn)的子節(jié)點(diǎn):新節(jié)點(diǎn)子節(jié)點(diǎn)ch和老節(jié)點(diǎn)子節(jié)點(diǎn)oldCh;
  2. ch和oldCh分別設(shè)置StartIdx(指向頭)和EndIdx(指向尾)變量,它們兩兩比較(按照sameNode方法),有四種方式來比較。如果4種方式都沒有匹配成功,如果設(shè)置了key就通過key進(jìn)行比較,在比較過程種startIdx++,endIdx--,一旦StartIdx > EndIdx表明ch或者oldCh至少有一個(gè)已經(jīng)遍歷完成,此時(shí)就會(huì)結(jié)束比較。

下面結(jié)合圖來理解:

第一步:

oldStartIdx = A , oldEndIdx = C;
newStartIdx = A , newEndIdx = D;

此時(shí)oldStartIdx和newStarIdx匹配,所以將dom中的A節(jié)點(diǎn)放到第一個(gè)位置,此時(shí)A已經(jīng)在第一個(gè)位置,所以不做處理,此時(shí)真實(shí)DOM順序:A  B  C;

第二步:

oldStartIdx = B , oldEndIdx = C;
newStartIdx = C , oldEndIdx = D;

此時(shí)oldEndIdx和newStartIdx匹配,將原本的C節(jié)點(diǎn)移動(dòng)到A后面,此時(shí)真實(shí)DOM順序:A   C   B;

第三步:

oldStartIdx = C , oldEndIdx = C;
newStartIdx = B , newEndIdx = D;
oldStartIdx++,oldEndIdx--;
oldStartIdx > oldEndIdx

此時(shí)遍歷結(jié)束,oldCh已經(jīng)遍歷完,那么將剩余的ch節(jié)點(diǎn)根據(jù)自己的index插入到真實(shí)DOM中即可,此時(shí)真實(shí)DOM順序:A  C  B  D;

所以匹配過程中判斷結(jié)束有兩個(gè)條件:

  • oldStartIdx > oldEndIdx表示oldCh先遍歷完成,如果ch有剩余節(jié)點(diǎn)就根據(jù)對(duì)應(yīng)index添加到真實(shí)DOM中;
  • newStartIdx > newEndIdx表示ch先遍歷完成,那么就要在真實(shí)DOM中將多余節(jié)點(diǎn)刪除掉;

看下圖這個(gè)實(shí)例,就是新節(jié)點(diǎn)先遍歷完成刪除多余節(jié)點(diǎn):

最后,在這些子節(jié)點(diǎn)sameVnode后如果滿足條件繼續(xù)執(zhí)行patchVnode,層層遞歸,直到oldVnode和Vnode中所有子節(jié)點(diǎn)都比對(duì)完成,也就把所有的補(bǔ)丁都打好了,此時(shí)更新到視圖。

總結(jié)

最后,用一張圖來記憶整個(gè)Diff過程,希望你能有所收獲!

彩蛋

因?yàn)镽eact只是簡單學(xué)了基礎(chǔ),這里作為對(duì)比來概述一下:

1.React渲染機(jī)制:React采用虛擬DOM,在每次屬性和狀態(tài)發(fā)生變化時(shí),render函數(shù)會(huì)返回不同的元素樹,然后對(duì)比返回的元素樹和上次渲染樹的差異并對(duì)差異部分進(jìn)行更新,最后渲染為真實(shí)DOM。

2.diff永遠(yuǎn)都是同層比較,如果節(jié)點(diǎn)類型不同,直接用新的替換舊的。如果節(jié)點(diǎn)類型相同,就比較他們的子節(jié)點(diǎn),依次類推。通常元素上綁定的key值就是用來比較節(jié)點(diǎn)的,所以一定要保證其唯一性,一般不采用數(shù)組下標(biāo)來作為key值,因?yàn)楫?dāng)數(shù)組元素發(fā)生變化時(shí)index會(huì)有所改動(dòng)。

3.渲染機(jī)制的整個(gè)過程包含了更新操作,將虛擬DOM轉(zhuǎn)換為真實(shí)DOM,所以整個(gè)渲染過程就是Reconciliation。而這個(gè)過程的核心又主要是diff算法,利用的是生命周期shouldComponentUpdate函數(shù)。

到此這篇帶你搞懂Vue虛擬Dom與diff算法的文章就介紹到這了,更多相關(guān)Vue虛擬Dom與diff算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue瀏覽器緩存sessionStorage+localStorage+Cookie區(qū)別解析

    Vue瀏覽器緩存sessionStorage+localStorage+Cookie區(qū)別解析

    這篇文章主要介紹了Vue瀏覽器緩存sessionStorage+localStorage+Cookie區(qū)別解析,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • Vue computed計(jì)算屬性總結(jié)記錄

    Vue computed計(jì)算屬性總結(jié)記錄

    在vue中,有時(shí)候你需要對(duì)data中的數(shù)據(jù)進(jìn)行處理,或者對(duì)抓取的數(shù)據(jù)進(jìn)行處理之后再掛載呈現(xiàn)到標(biāo)簽中,這時(shí)候你就需要計(jì)算屬性了,當(dāng)然看到這里你可能還是不了解那下面我舉幾個(gè)實(shí)例并附代碼解釋
    2023-02-02
  • vue3動(dòng)態(tài)路由+菜單欄的實(shí)現(xiàn)示例

    vue3動(dòng)態(tài)路由+菜單欄的實(shí)現(xiàn)示例

    在后臺(tái)管理系統(tǒng),可以根據(jù)登錄用戶的不同返回不同路由,頁面也會(huì)根據(jù)這些路由生成對(duì)應(yīng)的菜單,本文主要介紹了vue3動(dòng)態(tài)路由+菜單欄的實(shí)現(xiàn)示例,感興趣的可以了解一下
    2024-04-04
  • Vue前端實(shí)現(xiàn)截圖功能的簡單步驟

    Vue前端實(shí)現(xiàn)截圖功能的簡單步驟

    本文介紹了如何使用html2canvas庫來實(shí)現(xiàn)HTML頁面或某個(gè)元素的截圖功能,文中通過代碼介紹的非常詳細(xì),需要注意的是此方法只能在瀏覽器環(huán)境中使用,需要的朋友可以參考下
    2024-10-10
  • 淺談Vue3 Composition API如何替換Vue Mixins

    淺談Vue3 Composition API如何替換Vue Mixins

    這篇文章主要介紹了淺談Vue3 Composition API如何替換Vue Mixins,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • Vue2.0 ES6語法降級(jí)ES5的操作

    Vue2.0 ES6語法降級(jí)ES5的操作

    這篇文章主要介紹了Vue2.0 ES6語法降級(jí)ES5的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • 手把手搭建安裝基于windows的Vue.js運(yùn)行環(huán)境

    手把手搭建安裝基于windows的Vue.js運(yùn)行環(huán)境

    手把手教大家搭建安裝基于windows的Vue.js的運(yùn)行環(huán)境,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • 前端vue+element使用SM4國密加密解密的詳細(xì)實(shí)例

    前端vue+element使用SM4國密加密解密的詳細(xì)實(shí)例

    國密即國家密碼局認(rèn)定的國產(chǎn)密碼算法,主要有SM1,SM2,SM3,SM4,下面這篇文章主要給大家介紹了關(guān)于前端vue+element使用SM4國密加密解密的相關(guān)資料,需要的朋友可以參考下
    2023-03-03
  • 解決vue報(bào)錯(cuò):Do?not?mutate?vuex?store?state?outside?mutation?handlers問題

    解決vue報(bào)錯(cuò):Do?not?mutate?vuex?store?state?outside?mutati

    這篇文章主要介紹了解決vue報(bào)錯(cuò):Do?not?mutate?vuex?store?state?outside?mutation?handlers問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Vue組件傳值異步問題子組件拿到數(shù)據(jù)較慢解決

    Vue組件傳值異步問題子組件拿到數(shù)據(jù)較慢解決

    這篇文章主要為大家介紹了Vue組件傳值異步中子組件拿到數(shù)據(jù)較慢的問題解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08

最新評(píng)論

潞城市| 宜昌市| 马山县| 林西县| 灵川县| 元朗区| 涟水县| 阳信县| 三都| 镇江市| 田阳县| 宜君县| 兴国县| 石泉县| 乌苏市| 大同县| 五台县| 岗巴县| 延川县| 洛扎县| 蒙自县| 遵义市| 五华县| 东至县| 呼图壁县| 南丰县| 塔河县| 阿拉尔市| 彭山县| 巴林右旗| 鹤庆县| 亳州市| 读书| 庄浪县| 屏山县| 和林格尔县| 都昌县| 和田市| 东港市| 紫阳县| 黄骅市|