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

一文詳解Vue中的虛擬DOM與Diff算法

 更新時間:2024年02月22日 08:31:54   作者:沐渃清澄  
vue中的diff算法時常是面試過程中的考點,本文將為大家講解何為diff以及diff算法的實現(xiàn)過程,那么在了解diff之前,我們需要先了解虛擬DOM是什么,需要的朋友可以參考下

虛擬DOM

虛擬 DOM (Virtual DOM,簡稱 VDOM) 是一種編程概念,意為將目標所需的 UI 通過數(shù)據結構“虛擬”地表示出來,保存在內存中,然后將真實的DOM與之保持同步。具體來說,虛擬 DOM 是由一系列的 JavaScript 對象組成的樹狀結構,每個對象代表著一個DOM元素,包括元素的標簽名、屬性、子節(jié)點等信息。虛擬 DOM 中的每個節(jié)點都是一個 JavaScript 對象,它們可以輕松地被創(chuàng)建、更新和銷毀,而不涉及到實際的DOM操作。

主要作用

虛擬 DOM 的主要作用是在數(shù)據發(fā)生變化時,通過與上一次渲染的虛擬 DOM 進行對比,找出發(fā)生變化的部分,并最小化地更新實際 DOM。這種方式可以減少實際 DOM 操作的次數(shù),從而提高頁面渲染的性能和效率。
總的來說,虛擬 DOM 是一種用 JavaScript 對象模擬真實 DOM 結構和狀態(tài)的技術,它通過在內存中操作虛擬 DOM 樹來減少實際 DOM 操作,從而提高頁面的性能和用戶體驗。

虛擬DOM樹

顧名思義,也就是一個虛擬 DOM 作為根節(jié)點,包含有一個或多個的子虛擬 DOM。

Diff

在 Vue 3 中,diff(差異比較)是指在進行虛擬 DOM 更新時,對比新舊虛擬 DOM 樹的差異,然后只對實際發(fā)生變化的部分進行更新,以盡可能地減少對真實 DOM 的操作,提高頁面的性能和效率。diff整體策略為:深度優(yōu)先,同層比較。也就是說,比較只會在同層級進行, 不會跨層級比較;比較的過程中,循環(huán)從兩邊向中間收攏。

流程解析

Diff 算法的實現(xiàn)流程可以概括為以下幾個步驟:

  • 比較根節(jié)點: 首先,對比新舊虛擬 DOM 樹的根節(jié)點,判斷它們是否相同。

  • 逐層對比子節(jié)點: 如果根節(jié)點相同,則逐層對比子節(jié)點。

    • 比較子節(jié)點類型:

      • 如果節(jié)點類型不同,則直接替換整個節(jié)點。
      • 如果節(jié)點類型相同,繼續(xù)對比節(jié)點的屬性和事件。
    • 對比子節(jié)點列表:

      • 通過雙指針法對比新舊節(jié)點列表,查找相同位置的節(jié)點。
      • 如果節(jié)點相同,進行遞歸對比子節(jié)點。
      • 如果節(jié)點不同,根據情況執(zhí)行插入、刪除或移動節(jié)點的操作。
  • 處理新增、刪除和移動的節(jié)點:

    • 如果新節(jié)點列表中存在舊節(jié)點列表中沒有的節(jié)點,執(zhí)行新增操作。
    • 如果舊節(jié)點列表中存在新節(jié)點列表中沒有的節(jié)點,執(zhí)行刪除操作。
    • 如果新舊節(jié)點列表中都存在相同的節(jié)點,但順序不同,執(zhí)行移動節(jié)點的操作。
  • 更新節(jié)點屬性和事件:

    • 如果節(jié)點相同但屬性或事件發(fā)生了變化,更新節(jié)點的屬性和事件。
  • 遞歸對比子節(jié)點:

    • 如果節(jié)點類型相同且是容器節(jié)點(例如 div、ul 等),則遞歸對比子節(jié)點。

源碼解析

在源碼中patchVnode是diff發(fā)生的地方,下面是patchVnode的源碼:

function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {
  // 如果新舊節(jié)點一致,什么都不做
  if (oldVnode === vnode) {
    return
  }

  // 讓vnode.el引用到現(xiàn)在的真實dom,當el修改時,vnode.el會同步變化
  const elm = vnode.elm = oldVnode.elm

  // 異步占位符
  if (isTrue(oldVnode.isAsyncPlaceholder)) {
    if (isDef(vnode.asyncFactory.resolved)) {
      hydrate(oldVnode.elm, vnode, insertedVnodeQueue)
    } else {
      vnode.isAsyncPlaceholder = true
    }
    return
  }
  // 如果新舊都是靜態(tài)節(jié)點,并且具有相同的key
  // 當vnode是克隆節(jié)點或是v-once指令控制的節(jié)點時,只需要把oldVnode.elm和oldVnode.child都復制到vnode上
  // 也不用再有其他操作
  if (isTrue(vnode.isStatic) &&
    isTrue(oldVnode.isStatic) &&
    vnode.key === oldVnode.key &&
    (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
  ) {
    vnode.componentInstance = oldVnode.componentInstance
    return
  }

  let i
  const data = vnode.data
  if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
    i(oldVnode, vnode)
  }

  const oldCh = oldVnode.children
  const ch = vnode.children
  if (isDef(data) && isPatchable(vnode)) {
    for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)
    if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)
  }
  // 如果vnode不是文本節(jié)點或者注釋節(jié)點
  if (isUndef(vnode.text)) {
    // 并且都有子節(jié)點
    if (isDef(oldCh) && isDef(ch)) {
      // 并且子節(jié)點不完全一致,則調用updateChildren
      if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)

      // 如果只有新的vnode有子節(jié)點
    } else if (isDef(ch)) {
      if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
      // elm已經引用了老的dom節(jié)點,在老的dom節(jié)點上添加子節(jié)點
      addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)

      // 如果新vnode沒有子節(jié)點,而vnode有子節(jié)點,直接刪除老的oldCh
    } else if (isDef(oldCh)) {
      removeVnodes(elm, oldCh, 0, oldCh.length - 1)

      // 如果老節(jié)點是文本節(jié)點
    } else if (isDef(oldVnode.text)) {
      nodeOps.setTextContent(elm, '')
    }

    // 如果新vnode和老vnode是文本節(jié)點或注釋節(jié)點
    // 但是vnode.text != oldVnode.text時,只需要更新vnode.elm的文本內容就可以
  } else if (oldVnode.text !== vnode.text) {
    nodeOps.setTextContent(elm, vnode.text)
  }
  if (isDef(data)) {
    if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)
  }
}

以上代碼主要就是用于比較新舊虛擬 DOM 節(jié)點并進行更新。讓我逐步解釋這個函數(shù)的實現(xiàn):

  • 判斷是否需要更新: 首先,函數(shù)會比較新舊虛擬 DOM 節(jié)點是否相同,如果相同則直接返回,無需進行后續(xù)操作。
  • 獲取舊節(jié)點的真實 DOM 引用: 通過 elm = vnode.elm = oldVnode.elm 將新節(jié)點 vnode 的真實 DOM 引用指向舊節(jié)點的真實 DOM。
  • 處理異步占位符: 如果舊節(jié)點是異步占位符(asyncPlaceholder),并且新節(jié)點的異步工廠已經解析,則通過 hydrate 函數(shù)進行同步操作;否則,將新節(jié)點標記為異步占位符并返回。
  • 處理靜態(tài)節(jié)點: 如果新舊節(jié)點都是靜態(tài)節(jié)點(isStatic 為真),并且具有相同的 key,則將新節(jié)點的組件實例引用指向舊節(jié)點的組件實例。
  • 觸發(fā) prepatch 鉤子: 如果新節(jié)點的數(shù)據對象中定義了 hook 并且 prepatch 鉤子存在,則執(zhí)行該鉤子函數(shù),用于預處理新舊節(jié)點之間的差異。
  • 更新節(jié)點的屬性和事件: 如果新節(jié)點的數(shù)據對象中定義了 hook 并且 update 鉤子存在,則執(zhí)行該鉤子函數(shù),用于更新節(jié)點的屬性和事件。
  • 處理子節(jié)點: 如果新舊節(jié)點都有子節(jié)點,則比較它們之間的差異并進行更新,調用 updateChildren 函數(shù)。如果只有新節(jié)點有子節(jié)點,則將新節(jié)點的子節(jié)點添加到舊節(jié)點上。如果只有舊節(jié)點有子節(jié)點,則刪除舊節(jié)點的子節(jié)點。如果舊節(jié)點是文本節(jié)點,則清空其內容。
  • 更新文本內容: 如果新舊節(jié)點都是文本節(jié)點或注釋節(jié)點,并且它們的文本內容不同,則更新新節(jié)點的文本內容。
  • 觸發(fā) postpatch 鉤子: 如果新節(jié)點的數(shù)據對象中定義了 hook 并且 postpatch 鉤子存在,則執(zhí)行該鉤子函數(shù),用于處理節(jié)點更新后的操作。

Diff算法示例

下面是一個詳細的例子,假設有以下兩個虛擬 DOM 樹,我們將對它們進行 diff 算法的比較:

舊的虛擬 DOM 樹:

{
  type: 'div',
  props: { id: 'container' },
  children: [
    { type: 'p', props: { class: 'text' }, children: ['old Dom'] },
    { type: 'button', props: { disabled: true }, children: ['click'] }
  ]
}

新的虛擬 DOM 樹:

{
  type: 'div',
  props: { id: 'container' },
  children: [
    { type: 'p', props: { class: 'text' }, children: ['new DOM'] },
    { type: 'button', props: { disabled: false }, children: ['click'] },
    { type: 'span', props: { class: 'msg' }, children: ['msg'] }
  ]
}

Diff算法執(zhí)行:

  • 比較根節(jié)點:根節(jié)點相同,繼續(xù)比較子節(jié)點。

  • 比較子節(jié)點:

    • 第一個子節(jié)點類型相同,但內容不同,更新內容為 'new DOM'。
    • 第二個子節(jié)點相同,屬性發(fā)生變化,更新 disabled 屬性為 false。
    • 第三個子節(jié)點是新增節(jié)點,執(zhí)行插入操作。
  • 更新節(jié)點屬性和事件:第二個子節(jié)點的屬性發(fā)生變化,更新 disabled 屬性。

  • 遞歸對比子節(jié)點:針對新增的 span 節(jié)點,繼續(xù)遞歸對比其子節(jié)點。

最終結果:

    {
  type: 'div',
  props: { id: 'container' },
  children: [
    { type: 'p', props: { class: 'text' }, children: ['new DOM'] },
    { type: 'button', props: { disabled: false }, children: ['click'] },
     { type: 'span', props: { class: 'msg' }, children: ['msg'] }
  ]
}

結語

總的來說,Diff 算法的核心思想是Diff就是將新老虛擬DOM的不同點找到并生成一個補丁,并根據這個補丁生成更新操作,以最小化對實際 DOM 的操作,提高頁面渲染的性能和效率。通過深度優(yōu)先、同層比較的策略,Diff 算法能夠高效地處理虛擬 DOM 樹的更新,使得頁面在數(shù)據變化時能夠快速響應并更新對應的視圖。

以上就是一文詳解Vue中的虛擬DOM與Diff算法的詳細內容,更多關于Vue虛擬DOM與Diff算法的資料請關注腳本之家其它相關文章!

相關文章

  • vue的插槽原來該這樣理解

    vue的插槽原來該這樣理解

    這篇文章主要為大家詳細介紹了vue的插槽,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • vue3?TS?vite?element?ali-oss使用教程示例

    vue3?TS?vite?element?ali-oss使用教程示例

    這篇文章主要為大家介紹了vue3?TS?vite?element?ali-oss使用教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-07-07
  • el-form表單實現(xiàn)校驗的示例代碼

    el-form表單實現(xiàn)校驗的示例代碼

    本文主要介紹了el-form表單實現(xiàn)校驗的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-07-07
  • vue使用自定義icon圖標的方法

    vue使用自定義icon圖標的方法

    這篇文章主要介紹了vue使用自定義的icon圖標的方法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-05-05
  • vue中的row布局靠右對齊

    vue中的row布局靠右對齊

    這篇文章主要介紹了vue中的row布局靠右對齊,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • 詳解vue組件之間相互傳值的方式

    詳解vue組件之間相互傳值的方式

    這篇文章主要介紹了vue組件之間相互傳值的方式,對vue感興趣的同學,可以參考下
    2021-05-05
  • vue實現(xiàn)點擊復制到粘貼板

    vue實現(xiàn)點擊復制到粘貼板

    這篇文章主要為大家詳細介紹了vue實現(xiàn)點擊復制到粘貼板,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • vue3+vite 動態(tài)引用靜態(tài)資源及動態(tài)引入assets文件夾圖片的多種方式

    vue3+vite 動態(tài)引用靜態(tài)資源及動態(tài)引入assets文件夾圖片的多種方式

    通過require動態(tài)引入, 發(fā)現(xiàn)報錯:require is not defind,這是因為 require 是屬于 Webpack 的方法,本文給大家介紹vue3+vite 動態(tài)引用靜態(tài)資源及動態(tài)引入assets文件夾圖片的多種方式,感興趣的朋友一起看看吧
    2023-10-10
  • 使用vue實現(xiàn)加載頁

    使用vue實現(xiàn)加載頁

    這篇文章主要為大家詳細介紹了使用vue實現(xiàn)加載頁,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • 簡單學習vue指令directive

    簡單學習vue指令directive

    這篇文章主要和大家一起簡單學習一下vue指令:directive,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-11-11

最新評論

郯城县| 江西省| 华亭县| 喀什市| 西昌市| 青川县| 徐汇区| 罗城| 浪卡子县| 绥芬河市| 石河子市| 黄大仙区| 舞阳县| 鄂尔多斯市| 屯留县| 双鸭山市| 垣曲县| 隆子县| 塔城市| 利辛县| 鸡泽县| 安图县| 留坝县| 博客| 讷河市| 清水县| 万安县| 清水县| 香河县| 清原| 英山县| 新宁县| 孟津县| 府谷县| 兴海县| 林周县| 绥宁县| 安徽省| 肥西县| 拜泉县| 大关县|