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

vue 虛擬DOM快速入門

 更新時間:2021年04月20日 10:19:18   作者:彭加李  
這篇文章主要介紹了vue 虛擬DOM的相關(guān)資料,幫助大家更好的理解和學習使用vue框架,感興趣的朋友可以了解下

虛擬 DOM

什么是虛擬 dom

dom 是文檔對象模型,以節(jié)點樹的形式來表現(xiàn)文檔。

虛擬 dom 不是真正意義上的 dom。而是一個 javascript 對象。

正常的 dom 節(jié)點在 html 中是這樣表示:

<div class='testId'>
    <p>你好</p>
    <p>歡迎光臨</p>
</div>

而在虛擬 dom 中大概是這樣:

{
    tag: 'div',
    attributes:{
        class: ['testId']
    },
    children:[
        // p 元素
        // p 元素
    ]
}

我們可以將虛擬 dom 拆分成兩部分進行理解:虛擬 + dom。

  • 虛擬: 表示虛擬 dom 不是真正意義上的 dom,而是一個 javascript 對象;
  • dom: 表示虛擬 dom 能以類似節(jié)點樹的形式表示文檔。

虛擬 dom 的作用

現(xiàn)在主流的框架都是聲明式操作 dom 的框架。我們只需要描述狀態(tài)與 dom 之間的映射關(guān)系即可,狀態(tài)到視圖(真實的 dom)的轉(zhuǎn)換,框架會幫我們做。

最粗暴的做法是將狀態(tài)渲染成視圖,每次更新狀態(tài),都重新更新整個視圖。

這種做法的性能可想而知。比較好的想法是:狀態(tài)改變,只更新與狀態(tài)相關(guān)的 dom 節(jié)點。虛擬 dom 只是實現(xiàn)這個想法的其中一種方法而已。

具體做法:

  • 狀態(tài) -> 真實 dom(最初)
  • 狀態(tài) -> 虛擬 dom -> 真實 dom(使用虛擬 dom)

狀態(tài)改變,重新生成一份虛擬 dom,將上一份和這一份虛擬 dom 進行對比,找出需要更新的部分,更新真實 dom。

vue 中的虛擬 dom

真實的 dom 是由 節(jié)點(Node)組成,虛擬 dom 則是由虛擬節(jié)點(vNode)組成。

虛擬 dom 在 vue 中主要做兩件事:

  • 提供與真實節(jié)點(Node)對應(yīng)的虛擬節(jié)點(vNode)
  • 將新的虛擬節(jié)點與舊的虛擬節(jié)點進行對比,找出需要差異,然后更新視圖

“虛擬 DOM”是我們對由 Vue 組件樹建立起來的整個 VNode 樹的稱呼 —— vue 官網(wǎng)

vNode

什么是 vNode

上文提到,vNode(虛擬節(jié)點)對應(yīng)的是真實節(jié)點(Node)。

vNode 可以理解成節(jié)點描述對象。描述了如何創(chuàng)建真實的 dom 節(jié)點。

vue.js 中有一個 vNode 類??梢允褂盟鼊?chuàng)建不同類型的 vNode 實例,不同類型的 vNode 對應(yīng)著不同類型的 dom 元素。代碼如下:

export default class VNode {
   constructor (
    tag?: string,
    data?: VNodeData,
    children?: ?Array<VNode>,
    text?: string,
    elm?: Node,
    context?: Component,
    componentOptions?: VNodeComponentOptions,
    asyncFactory?: Function
  ) {
    this.tag = tag
    this.data = data
    this.children = children
    this.text = text
    this.elm = elm
    this.ns = undefined
    this.context = context
    this.fnContext = undefined
    this.fnOptions = undefined
    this.fnScopeId = undefined
    this.key = data && data.key
    this.componentOptions = componentOptions
    this.componentInstance = undefined
    this.parent = undefined
    this.raw = false
    this.isStatic = false
    this.isRootInsert = true
    this.isComment = false
    this.isCloned = false
    this.isOnce = false
    this.asyncFactory = asyncFactory
    this.asyncMeta = undefined
    this.isAsyncPlaceholder = false
  }

  get child (): Component | void {
    return this.componentInstance
  }
}

從代碼不難看出 vNode 類創(chuàng)建的實例,本質(zhì)上就是一個普通的 javascript 對象。

vNode 的類型

前面我們已經(jīng)介紹通過 vNode 類可以創(chuàng)建不同類型的 vNode。而不同類型的 vNode 是由有效屬性區(qū)分。例如 isComment = true 表示注釋節(jié)點;isCloned = true 表示克隆節(jié)點等等。

vNode 類型有:注釋節(jié)點、文本節(jié)點、克隆節(jié)點、元素節(jié)點、組件節(jié)點。

以下是注釋節(jié)點、文本節(jié)點和克隆節(jié)點的代碼:

/*
注釋節(jié)點
有效屬性:{isComment: true, text: '注釋節(jié)點'}
*/
export const createEmptyVNode = (text: string = '') => {
  const node = new VNode()
  node.text = text
  // 注釋
  node.isComment = true
  return node
}
/*
文本節(jié)點
有效屬性:{text: '文本節(jié)點'}
*/
export function createTextVNode (val: string | number) {
  return new VNode(undefined, undefined, undefined, String(val))
}

// optimized shallow clone
// used for static nodes and slot nodes because they may be reused across
// 用于靜態(tài)節(jié)點和插槽節(jié)點
// multiple renders, cloning them avoids errors when DOM manipulations rely
// on their elm reference.
// 克隆節(jié)點
export function cloneVNode (vnode: VNode): VNode {
  const cloned = new VNode(
    vnode.tag,
    vnode.data,
    // #7975
    // clone children array to avoid mutating original in case of cloning
    // a child.
    vnode.children && vnode.children.slice(),
    vnode.text,
    vnode.elm,
    vnode.context,
    vnode.componentOptions,
    vnode.asyncFactory
  )
  cloned.ns = vnode.ns
  cloned.isStatic = vnode.isStatic
  cloned.key = vnode.key
  cloned.isComment = vnode.isComment
  cloned.fnContext = vnode.fnContext
  cloned.fnOptions = vnode.fnOptions
  cloned.fnScopeId = vnode.fnScopeId
  cloned.asyncMeta = vnode.asyncMeta
  // 標記是克隆節(jié)點
  cloned.isCloned = true
  return cloned
}

克隆節(jié)點其實就是將現(xiàn)有節(jié)點的所有屬性賦值到新節(jié)點中,最后用 cloned.isCloned = true 標記自身是克隆節(jié)點。

元素節(jié)點通常有以下 4 個屬性:

  • tag:節(jié)點名稱。例如 div、p
  • data:節(jié)點上的數(shù)據(jù)。例如 class、style
  • children:子節(jié)點
  • context:在組件內(nèi)呈現(xiàn)

組件節(jié)點與元素節(jié)點類似,包含兩個獨有的屬性:

  • componentOptions:組件節(jié)點的選項參數(shù),例如propsData、listeners、children、tag
  • componentInstance:組件的實例

patch

前面已經(jīng)介紹了虛擬 dom 在 vue 中做的第一件事:提供與真實節(jié)點(Node)對應(yīng)的虛擬節(jié)點(vNode);接下來介紹第二件事:將新的虛擬節(jié)點與舊的虛擬節(jié)點進行對比,找出需要差異,然后更新視圖。

第二件事在 vue 中的實現(xiàn)叫做 patch,即打補丁、修補的意思。通過對比新舊 vNode,找出差異,然后在現(xiàn)有 dom 的基礎(chǔ)上進行修補,從而實現(xiàn)視圖更新。

對比 vNode 找差異是手段,更新視圖才是目的。

而更新視圖無非就是新增節(jié)點、刪除節(jié)點和更新節(jié)點。接下來我們逐一分析什么時候新增節(jié)點、在哪里新增;什么時候刪除節(jié)點,刪除哪個;什么時候更新節(jié)點,更新哪個;

注:當 vNode 與 oldVNode 不相同的時候,以 vNode 為準。

新增節(jié)點

一種情況是:vNode 存在而 oldVNode 不存在時,需要新增節(jié)點。最典型的是初次渲染,因為 odlVNode 是不存在的。

另一種情況是 vNode 與 oldVNode 完全不是同一個節(jié)點。這時就需要使用 vNode 生成真實的 dom 節(jié)點并插入到 oldVNode 指向的真實 dom 節(jié)點旁邊。oldVNode 則是一個被廢棄的節(jié)點。例如下面這種情況:

<div>
  <p v-if="type === 'A'">
    我是節(jié)點A
  </p>
  <span v-else-if="type === 'B'">
    我是與A完全不同的節(jié)點B
  </span>
</div>

當 type 由 A 變?yōu)?B,節(jié)點就會從 p 變成 span,由于 vNode 與 oldVNode 完全不是同一個節(jié)點,所以需要新增節(jié)點。

刪除節(jié)點

當節(jié)點只在 oldVNode 中存在時,直接將其刪除即可。

更新節(jié)點

前面介紹了新增節(jié)點和刪除節(jié)點的場景,發(fā)現(xiàn)它們有一個共同點:vNode 與 oldVNode 完全不相同。

但更常見的場景是 vNode 與 oldVNode 是同一個節(jié)點。然后我們需要對它們(vNode 與 oldVNode)進行一個更細致的對比,再對 oldVNode 對應(yīng)的真實節(jié)點進行更新。

對于文本節(jié)點,邏輯自然簡單。首先對比新舊 vNode,發(fā)現(xiàn)是同一個節(jié)點,然后將 oldVNode 對應(yīng)的 dom 節(jié)點的文本改成 vNode 中的文本即可。但對于復(fù)雜的 vNode,比如界面中的一顆樹組件,這個過程就會變得復(fù)雜。

新增節(jié)點 - 源碼分析

思考一下:前面說到 vNode 的類型有:注釋節(jié)點、文本節(jié)點、克隆節(jié)點、元素節(jié)點、組件節(jié)點。請問這幾種類型都會被創(chuàng)建并插入到 dom 中嗎?

答:只有注釋節(jié)點、文本節(jié)點、元素節(jié)點。因為 html 只認識這幾種。

由于只有上面三種節(jié)點類型,根據(jù)類型做響應(yīng)的創(chuàng)建,然后插入對應(yīng)的位置即可。

以元素節(jié)點為例,如果 vNode 有 tag 屬性,則說明是元素節(jié)點。則調(diào)用 createElement 方法創(chuàng)建對應(yīng)的節(jié)點,接下來就通過 appendChild 方法插入到指定父節(jié)點中。如果父元素已經(jīng)在視圖中,那么把元素插入到它下面將會自動渲染出來;如果 vNode 的 isComment 屬性是 true,則表示注釋節(jié)點;都不是則是文本節(jié)點;

通常元素里面會有子節(jié)點,所以這里涉及一個遞歸的過程,也就是將 vNode 中的 children 依次遍歷,創(chuàng)建節(jié)點,然后插入到父節(jié)點(父節(jié)點也就是剛剛創(chuàng)建出的 dom 節(jié)點)中,一層一層的遞歸進行。

請看源碼:

// 創(chuàng)建元素
function createElm (
  vnode,
  insertedVnodeQueue,
  parentElm,
  refElm,
  nested,
  ownerArray,
  index
) {
  if (isDef(vnode.elm) && isDef(ownerArray)) {
    // This vnode was used in a previous render!
    // now it's used as a new node, overwriting its elm would cause
    // potential patch errors down the road when it's used as an insertion
    // reference node. Instead, we clone the node on-demand before creating
    // associated DOM element for it.
    vnode = ownerArray[index] = cloneVNode(vnode);
  }

  vnode.isRootInsert = !nested; // for transition enter check
  if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
    return
  }

  var data = vnode.data;
  var children = vnode.children;
  var tag = vnode.tag;
  // 有 tag 屬性,表示是元素節(jié)點
  if (isDef(tag)) {
    vnode.elm = vnode.ns
      ? nodeOps.createElementNS(vnode.ns, tag)
      // 創(chuàng)建元素。nodeOps 涉及到跨平臺
      : nodeOps.createElement(tag, vnode);
    setScope(vnode);

    /* istanbul ignore if */
    {
      // 遞歸創(chuàng)建子節(jié)點,并將子節(jié)點插入到父節(jié)點上
      createChildren(vnode, children, insertedVnodeQueue);
      if (isDef(data)) {
        invokeCreateHooks(vnode, insertedVnodeQueue);
      }
      // 將 vnode 對應(yīng)的元素插入到父元素中
      insert(parentElm, vnode.elm, refElm);
    }

  // isComment 屬性表示注釋節(jié)點
  } else if (isTrue(vnode.isComment)) {
    vnode.elm = nodeOps.createComment(vnode.text);
    // 插入父節(jié)點
    insert(parentElm, vnode.elm, refElm);
  // 否則就是子節(jié)點
  } else {
    vnode.elm = nodeOps.createTextNode(vnode.text);
    // 插入父節(jié)點
    insert(parentElm, vnode.elm, refElm);
  }
}

// 遞歸創(chuàng)建子節(jié)點,并將子節(jié)點插入到父節(jié)點上。vnode 表示父節(jié)點
function createChildren (vnode, children, insertedVnodeQueue) {
  if (Array.isArray(children)) {
    if (process.env.NODE_ENV !== 'production') {
      checkDuplicateKeys(children);
    }
    // 依次創(chuàng)建子節(jié)點,并將子節(jié)點插入到父節(jié)點中
    for (var i = 0; i < children.length; ++i) {
      createElm(children[i], insertedVnodeQueue, vnode.elm, null, true, children, i);
    }
  } else if (isPrimitive(vnode.text)) {
    nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(String(vnode.text)));
  }
}

刪除節(jié)點 - 源碼分析

刪除節(jié)點非常簡單。直接看源碼:

// 刪除一組指定節(jié)點
function removeVnodes (parentElm, vnodes, startIdx, endIdx) {
  for (; startIdx <= endIdx; ++startIdx) {
    var ch = vnodes[startIdx];
    if (isDef(ch)) {
      if (isDef(ch.tag)) {
        removeAndInvokeRemoveHook(ch);
        invokeDestroyHook(ch);
      } else { // Text node
        // 刪除個節(jié)點
        removeNode(ch.elm);
      }
    }
  }
}

// 刪除單個節(jié)點
function removeNode (el) {
  var parent = nodeOps.parentNode(el);
  // element may have already been removed due to v-html / v-text
  if (isDef(parent)) {
    // nodeOps里封裝了跨平臺的方法
    nodeOps.removeChild(parent, el);
  }
}

以上就是vue 虛擬DOM快速入門的詳細內(nèi)容,更多關(guān)于vue 虛擬DOM的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue中使用console.log打印的實現(xiàn)

    vue中使用console.log打印的實現(xiàn)

    這篇文章主要介紹了vue中使用console.log打印的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • 利用vue開發(fā)一個所謂的數(shù)獨方法實例

    利用vue開發(fā)一個所謂的數(shù)獨方法實例

    數(shù)獨是源自18世紀瑞士的一種數(shù)學游戲,是一種運用紙、筆進行演算的邏輯游戲。下面這篇文章主要給大家介紹了關(guān)于利用vue開發(fā)一個所謂的數(shù)獨的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下。
    2017-12-12
  • 關(guān)于vue.js彈窗組件的知識點總結(jié)

    關(guān)于vue.js彈窗組件的知識點總結(jié)

    最近在開發(fā)過程對對于組件化的開發(fā)有一些感想,于是開始記錄下這些。彈窗組件一直是 web 開發(fā)中必備的,使用頻率相當高,最常見的莫過于 alert,confirm和prompt這些,不同的組件庫對于彈窗的處理也是不一樣的,下面來一起看看吧。
    2016-09-09
  • 詳解nuxt sass全局變量(公共scss解決方案)

    詳解nuxt sass全局變量(公共scss解決方案)

    這篇文章主要介紹了詳解nuxt sass全局變量(公共scss解決方案),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06
  • vue實現(xiàn)登錄時圖形驗證碼

    vue實現(xiàn)登錄時圖形驗證碼

    這篇文章主要為大家詳細介紹了vue實現(xiàn)登錄時圖形驗證碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 解決axios發(fā)送post請求返回400狀態(tài)碼的問題

    解決axios發(fā)送post請求返回400狀態(tài)碼的問題

    今天小編就為大家分享一篇解決axios發(fā)送post請求返回400狀態(tài)碼的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • Vue高級組件之函數(shù)式組件的使用場景與源碼分析

    Vue高級組件之函數(shù)式組件的使用場景與源碼分析

    Vue提供了一種稱為函數(shù)式組件的組件類型,用來定義那些沒有響應(yīng)數(shù)據(jù),也不需要有任何生命周期的場景,它只接受一些props來顯示組件,下面這篇文章主要給大家介紹了關(guān)于Vue高級組件之函數(shù)式組件的使用場景與源碼分析的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • 基于 vue-skeleton-webpack-plugin 的骨架屏實戰(zhàn)

    基于 vue-skeleton-webpack-plugin 的骨架屏實戰(zhàn)

    這篇文章主要介紹了基于 vue-skeleton-webpack-plugin 的骨架屏實戰(zhàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-08-08
  • Vue+Openlayer實現(xiàn)圖形的拖動和旋轉(zhuǎn)變形效果

    Vue+Openlayer實現(xiàn)圖形的拖動和旋轉(zhuǎn)變形效果

    Openlayer具有自己的擴展插件ol-ext,可以用來實現(xiàn)圖形的拖拽、旋轉(zhuǎn)、縮放、拉伸、移動等操作,本文將主要介紹通過Openlayer實現(xiàn)圖形的拖動和旋轉(zhuǎn),需要的同學可以學習一下
    2021-11-11
  • vue組件間傳值的方法你知道幾種

    vue組件間傳值的方法你知道幾種

    這篇文章主要為大家詳細介紹了vue組件間傳值的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02

最新評論

鸡泽县| 高平市| 习水县| 曲靖市| 郑州市| 东乡族自治县| 藁城市| 开鲁县| 新沂市| 宝坻区| 黄龙县| 彩票| 敦煌市| 镇巴县| 永顺县| 繁峙县| 黎平县| 方城县| 安西县| 巴马| 方城县| 额济纳旗| 洪湖市| 资中县| 法库县| 广饶县| 韶山市| 衡南县| 玛曲县| 桐庐县| 迭部县| 嘉鱼县| 光山县| 达州市| 保山市| 环江| 蓬莱市| 日土县| 兴山县| 南澳县| 庆城县|