欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入聊一聊虛擬DOM與diff算法

 更新時(shí)間:2022年04月19日 10:43:51   作者:豆豆打丑小鴨  
為什么vue等的這些mvvm框架比傳統(tǒng)的操作dom渲染頁(yè)面要快,下面這篇文章主要給大家介紹了關(guān)于虛擬DOM與diff算法的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下

虛擬DOM與diff算法

在vue、react等技術(shù)出現(xiàn)之前,每次修改DOM都需要通過(guò)遍歷查詢DOM樹(shù)的方式,找到需要更新的DOM,然后修改樣式或結(jié)構(gòu),資源損耗十分嚴(yán)重。而對(duì)于虛擬DOM來(lái)說(shuō),每次DOM的更改就變成了JS對(duì)象的屬性的更改,能方便的查找JS對(duì)象的屬性變化,要比查詢DOM樹(shù)的性能開(kāi)銷(xiāo)小,所以能夠改善瀏覽器的性能問(wèn)題。

對(duì)于vue,從vue2就開(kāi)始支持虛擬DOM。

diff算法:簡(jiǎn)單來(lái)說(shuō)就是找出兩個(gè)對(duì)象的差異,只對(duì)有差異的一小塊DOM進(jìn)行更新,而不是整個(gè)DOM,從而達(dá)到最小量更新的效果

虛擬DOM:內(nèi)部會(huì)把代碼段解析成一個(gè)對(duì)象(真實(shí)DOM是通過(guò)模板編譯變成虛擬DOM的)

用JS對(duì)象描述DOM的層次結(jié)構(gòu),DOM中的一切屬性都在虛擬DOM中有對(duì)應(yīng)的屬性

snabbdom環(huán)境搭建

是虛擬DOM庫(kù),diff算法的鼻祖,vue源碼借鑒了snabbdom

官方git:https://github.com/snabbdom/snabbdom

git上的snabbdom源碼是用TypeScript寫(xiě)的,如果要直接使用編譯出來(lái)的Javascript版的snabbdom庫(kù),可以從npm上下載npm i -D snabbdom

snabbdom庫(kù)是DOM庫(kù),不能在node js環(huán)境運(yùn)行,需要搭建webpack和webpack-dev-server開(kāi)發(fā)環(huán)境。需要注意的是必須安裝webpack@5。

npm i -D webpack@5 webpack-cli@3 webpack-dev-server@3

配置webpack.config.js文件,參考官網(wǎng)進(jìn)行配置:https://webpack.docschina.org/

const path = require('path');

module.exports = {
    // 入口
    entry: './src/index.js',

    // 出口
    output: {
        // 虛擬打包路徑,文件夾不會(huì)真正生成,而是在8080端口虛擬生成
        publicPath: 'xuni',
        // 打包出來(lái)的文件名
        filename: "bundle.js",
    },
    // 配置webpack-dev-server
    devServer: {
        // 端口號(hào)
        port: 8082,
        // 靜態(tài)根目錄
        contentBase: 'www',
    },
}

將項(xiàng)目根目錄下的package.json文件中修改script的配置,就可以通過(guò)npm run dev啟動(dòng)項(xiàng)目

配置完之后將官網(wǎng)的Example進(jìn)行測(cè)試,由于示例要獲取id=container的節(jié)點(diǎn),所以我們需要提前準(zhǔn)備一個(gè)id為container的div。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div id="container"></div>
    <script src="/xuni/bundle.js"></script>
</body>
</html>

需要注意的點(diǎn)

頁(yè)面出現(xiàn)如下?tīng)顟B(tài)即表示配置完成

虛擬DOM和h函數(shù)

diff是發(fā)生在虛擬DOM上的

新虛擬DOM和老虛擬DOM進(jìn)行diff(精細(xì)化比較),算出應(yīng)該如何最小量更新,最后反映到真正的DOM上。

h函數(shù)用來(lái)產(chǎn)生虛擬節(jié)點(diǎn)(vnode)

h('a',{ props: { href: 'https://www.baidu.com' } }, '百度');

會(huì)得到這樣的虛擬節(jié)點(diǎn)

{ "sel": "a", "data": { props: { href: 'https://www.baidu.com' } }, "text": "百度" }

它表示的真正的DOM節(jié)點(diǎn)

<a >百度</a>

如果需要讓虛擬節(jié)點(diǎn)上樹(shù),需要借助patch函數(shù)

import {
    init,
    classModule,
    propsModule,
    styleModule,
    eventListenersModule,
    h,
} from "snabbdom";

// 創(chuàng)建patch函數(shù)
var patch = init([classModule, propsModule, styleModule, eventListenersModule]);

//創(chuàng)建虛擬節(jié)點(diǎn)
var vNode1 = h('a',{ props: { href: 'https://www.baidu.com' } }, '百度');

// 讓虛擬節(jié)點(diǎn)上樹(shù)
const container = document.getElementById('container');
patch(container,vNode1);

h函數(shù)可以嵌套使用,從而得到虛擬DOM樹(shù)

h('ul',{},[
	h('li',{},'可樂(lè)');
	h('li',{},'雪碧');
	h('li',{},'椰汁');
])

diff算法

實(shí)現(xiàn)最小量更新。需要key。key是這個(gè)節(jié)點(diǎn)的唯一標(biāo)識(shí),告訴diff算法,在更改前后它們是同一個(gè)DOM節(jié)點(diǎn)。

只有同一個(gè)虛擬節(jié)點(diǎn),才進(jìn)行精細(xì)化比較。否則就是暴力刪除舊的、插入新的。

同一個(gè)虛擬節(jié)點(diǎn):選擇器相同且key相同

只進(jìn)行同層比較,不會(huì)進(jìn)行跨層比較。

比如下面這兩個(gè)DOM節(jié)點(diǎn),雖然是同一片虛擬節(jié)點(diǎn),但是跨層了,依舊會(huì)暴力刪除舊的、插入新的。

const vnode1 = h('div',{},[
	h('p',{ key: 'A' }, 'A'),
	h('p',{ key: 'B' }, 'B'),
	h('p',{ key: 'C' }, 'C'),
	h('p',{ key: 'D' }, 'D'),
]);

const vnode2 = h('div',{},h('section',{},[
	h('p',{ key: 'A' }, 'A'),
	h('p',{ key: 'B' }, 'B'),
	h('p',{ key: 'C' }, 'C'),
	h('p',{ key: 'D' }, 'D'),
]))

分析源碼也可以驗(yàn)證以上所述

首先會(huì)去判斷是不是虛擬節(jié)點(diǎn),不是的話會(huì)先去把它包裝成虛擬節(jié)點(diǎn)

然后判斷是不是同一個(gè)節(jié)點(diǎn),不是的話插入新的、刪除舊的,是的話精細(xì)化比較

執(zhí)行的流程圖

patch函數(shù)

首先判斷oldVnode是否是虛擬節(jié)點(diǎn),如果是DOM節(jié)點(diǎn)的話先把oldVnode包裝成虛擬節(jié)點(diǎn)

然后判斷新節(jié)點(diǎn)和舊節(jié)點(diǎn)是否是同一個(gè)節(jié)點(diǎn),判斷key的值是否相同,標(biāo)簽名是否相同,是否都定義了data(data包含一些具體的信息,onclick、style等)

如果不是同一節(jié)點(diǎn),新節(jié)點(diǎn)直接替換老節(jié)點(diǎn),刪除舊的、插入新的。在源碼中,創(chuàng)建所有的子節(jié)點(diǎn)時(shí),需要遞歸。

如果新舊節(jié)點(diǎn)是同一個(gè)節(jié)點(diǎn)時(shí),會(huì)執(zhí)行patchVnode進(jìn)行子節(jié)點(diǎn)比較

patchVnode函數(shù)

首先會(huì)找到對(duì)應(yīng)的真實(shí)DOM

const elm = (vnode.elm = oldVnode.elm)!;

如果新老節(jié)點(diǎn)相同,直接返回 if(oldVnode === vnode) return

  • 如果vnode沒(méi)有文本節(jié)點(diǎn)(isUndef(vnode.text))

    • 都有children且不相同

      使用updateChildren對(duì)比children(diff算法的核心)

    • 只有vnode有children

      則oldVnode是一個(gè)空標(biāo)簽或者是文本節(jié)點(diǎn),如果是文本節(jié)點(diǎn)就清空文本節(jié)點(diǎn),然后將vnode的children創(chuàng)建為真實(shí)DOM后插入到空標(biāo)簽內(nèi)。

    • 只有oldVnode有children

      vnode沒(méi)有的東西,在oldVnode內(nèi)需要?jiǎng)h除掉removeVnodes(oldVnode有且vnode沒(méi)有的,都清空或移除)

    • 只有oldVnode有文本

      清空文本

  • 如果vnode有text屬性且不同

    以vnode為標(biāo)準(zhǔn),無(wú)論oldVnode是什么類(lèi)型節(jié)點(diǎn),直接設(shè)置為vnode內(nèi)的文本

updateChildren函數(shù)

updateChildren方法的核心:

  • 提取出新老節(jié)點(diǎn)的子節(jié)點(diǎn):新節(jié)點(diǎn)子節(jié)點(diǎn)ch和老節(jié)點(diǎn)子節(jié)點(diǎn)oldCh

  • ch和oldCh分別設(shè)置StartIdx(頭指針)和EndIdx(尾指針)變量,相互比較。此時(shí)就有四個(gè)變量:oldStartIdx、oldEndIdx、newStartIdx、newEndIdx(這里采用雙指針的思想)

    有四種方式來(lái)比較:

    oldStartIdx和newStartIdx比較

    如果匹配,DOM不用修改,將oldStartIdx和newStartIdx的下標(biāo)往后移一位

    oldEndIdx和newEndIdx比較

    如果匹配,DOM不用修改,將oldEndIdx和newEndIdx的下標(biāo)往前移一位

    oldStartIdx和newEndIdx比較

    如果匹配,DOM不用修改,將oldStartIdx對(duì)應(yīng)的真實(shí)DOM插入到最后一位,oldStartIdx的下標(biāo)后移一位,newEndIdx的下標(biāo)前移一位。

    oldEndIdx和newStartIdx比較

    如果匹配,DOM不用修改,將oldEndIdx對(duì)應(yīng)的真實(shí)DOM插入到oldEndIdx對(duì)應(yīng)真實(shí)Dom的前面,oldEndIdx的下標(biāo)前移一位,newStartIdx的下標(biāo)后移一位。

如果4種方式都沒(méi)有匹配成功,如果設(shè)置了key就通過(guò)key進(jìn)行比較,在比較過(guò)程中startIdx++,endIdx--,一旦StartIdx > EndIdx表明ch或者oldCh至少有一個(gè)已經(jīng)遍歷完成,此時(shí)就會(huì)結(jié)束比較

處理結(jié)束后,如果新節(jié)點(diǎn)有剩余,就添加;如果舊節(jié)點(diǎn)有剩余,就刪除

v-for中key作用與原理

key是虛擬DOM對(duì)象的標(biāo)識(shí),當(dāng)數(shù)據(jù)發(fā)生變化時(shí),Vue會(huì)根據(jù)【新數(shù)據(jù)】產(chǎn)生【新的虛擬DOM】,隨后Vue進(jìn)行【新虛擬DOM】與【舊虛擬DOM】的差異比較,比較規(guī)則如下:

(1)舊虛擬DOM中找到了與新虛擬DOM相同的key

?①若虛擬DOM中內(nèi)容沒(méi)變,直接使用之前的真實(shí)DOM

?②若虛擬DOM中內(nèi)容變了,則生成新的真實(shí)DOM,隨后替換掉頁(yè)面中之前的真實(shí)DOM

(2)舊虛擬DOM中未找到與新虛擬DOM相同的key

?創(chuàng)建新的真實(shí)DOM,隨后渲染到頁(yè)面

所以,如果用index作為key可能會(huì)引發(fā)的問(wèn)題:

(1)若對(duì)數(shù)據(jù)進(jìn)行:逆序添加、逆序刪除等破環(huán)順序操作,會(huì)產(chǎn)生沒(méi)有必要的真實(shí)DOM更新==>界面效果沒(méi)問(wèn)題,但效率低

(2)如果結(jié)構(gòu)中還包含輸入類(lèi)的DOM,會(huì)產(chǎn)生錯(cuò)誤DOM錯(cuò)誤==>界面有問(wèn)題

實(shí)際開(kāi)發(fā)中如何選擇key

最好使用每條數(shù)據(jù)的唯一標(biāo)識(shí)作為key,比如id、手機(jī)號(hào)、身份證號(hào)、學(xué)號(hào)等唯一值如果不存在對(duì)數(shù)據(jù)的逆序添加、逆序刪除等破壞順序操作,僅用于渲染列表用于展示,使用index作為key是沒(méi)有問(wèn)題的

總結(jié)

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

相關(guān)文章

  • vue中插槽整理及用法分析

    vue中插槽整理及用法分析

    在本篇文章里小編給大家整理的是一篇關(guān)于vue中插槽整理及用法分析內(nèi)容,對(duì)此有興趣的朋友們可以跟著學(xué)習(xí)下。
    2021-12-12
  • 一文徹底搞懂Vue中scoped和/deep/原理

    一文徹底搞懂Vue中scoped和/deep/原理

    在Vue中,有兩種常用的CSS選擇器,用于修改組件樣式:scoped?和?/deep/(或?::v-deep),它們都是為了實(shí)現(xiàn)樣式的作用域,本文小編就來(lái)分別給大家介紹一下這兩種選擇器的原理,需要的朋友可以參考下
    2023-08-08
  • 源碼剖析Vue3中如何進(jìn)行錯(cuò)誤處理

    源碼剖析Vue3中如何進(jìn)行錯(cuò)誤處理

    錯(cuò)誤處理是框架設(shè)計(jì)的核心要素之一,框架的錯(cuò)誤處理好壞,直接決定用戶應(yīng)用程序的健壯性以及用戶開(kāi)發(fā)應(yīng)用時(shí)處理錯(cuò)誤的心智負(fù)擔(dān),本文將從源碼入手,剖析一下Vue3中是如何進(jìn)行錯(cuò)誤處理的,需要的可以參考下
    2024-01-01
  • vue3自定義dialog、modal組件的方法

    vue3自定義dialog、modal組件的方法

    這篇文章主要介紹了vue3自定義dialog、modal組件的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • vue獲取token實(shí)現(xiàn)token登錄的示例代碼

    vue獲取token實(shí)現(xiàn)token登錄的示例代碼

    最近新做了個(gè)vue項(xiàng)目,正好項(xiàng)目中有登錄部分,本文就詳細(xì)的介紹一下登錄部分的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),感興趣的小伙伴們可以參考一下
    2021-11-11
  • Vue3中列表拖拽排序的實(shí)現(xiàn)示例

    Vue3中列表拖拽排序的實(shí)現(xiàn)示例

    本文主要介紹了Vue3中列表拖拽排序的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • vue3中使用ant-design-vue的layout組件實(shí)現(xiàn)動(dòng)態(tài)導(dǎo)航欄和面包屑功能

    vue3中使用ant-design-vue的layout組件實(shí)現(xiàn)動(dòng)態(tài)導(dǎo)航欄和面包屑功能

    這篇文章主要介紹了vue3中使用ant-design-vue的layout組件實(shí)現(xiàn)動(dòng)態(tài)導(dǎo)航欄和面包屑功能,基于一個(gè)新建的Vue3項(xiàng)目上實(shí)現(xiàn),本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • vue實(shí)現(xiàn)導(dǎo)出excel的多種方式總結(jié)

    vue實(shí)現(xiàn)導(dǎo)出excel的多種方式總結(jié)

    在Vue中實(shí)現(xiàn)導(dǎo)出Excel有多種方式,可以通過(guò)前端實(shí)現(xiàn),也可以通過(guò)前后端配合實(shí)現(xiàn),這篇文章將為大家詳細(xì)介紹幾種常用的實(shí)現(xiàn)方式,需要的可以參考下
    2023-08-08
  • 使用provide/inject實(shí)現(xiàn)跨組件通信的方法

    使用provide/inject實(shí)現(xiàn)跨組件通信的方法

    在 Vue 應(yīng)用中,組件間通信是構(gòu)建復(fù)雜應(yīng)用時(shí)的一個(gè)常見(jiàn)需求,Vue3.x 提供了provide和inject API,讓跨組件通信變得更加簡(jiǎn)潔和高效,本文將深入探討如何使用provide和inject在Vue3.x中實(shí)現(xiàn)跨組件通信,并通過(guò)示例代碼一步步進(jìn)行說(shuō)明,需要的朋友可以參考下
    2024-03-03
  • Vue實(shí)現(xiàn)可復(fù)用輪播組件的方法

    Vue實(shí)現(xiàn)可復(fù)用輪播組件的方法

    這篇文章主要為大家詳細(xì)介紹了Vue實(shí)現(xiàn)可復(fù)用輪播組件的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07

最新評(píng)論