91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue3中如何改進VDOM

發布時間:2020-07-31 10:46:04 來源:億速云 閱讀:159 作者:小豬 欄目:web開發

這篇文章主要為大家展示了Vue3中如何改進VDOM,內容簡而易懂,希望大家可以學習一下,學習完之后肯定會有收獲的,下面讓小編帶大家一起來看看吧。

前言

vue-next 對virtual dom的patch更新做了一系列的優化,從編譯時加入了 block 以減少 vdom 之間的對比次數,另外還有 hoisted 的操作減少了內存的開銷。本文寫給自己看,做個知識點記錄,如有錯誤,還請不吝賜教。

VDOM

VDOM的概念簡單來說就是用js對象來模擬真實DOM樹。由于MV**的架構,真實DOM樹應該隨著數據(Vue2.x中的data)的改變而發生改變,這些改變可能是以下幾個方面:

  • v-if
  • v-for
  • 動態的props(如:class,@click)
  • 子節點的改變
  • 等等

Vue框架要做的其實很單一:在用戶改變數據時,正確更新DOM樹,做法就是其核心的VDOM的patch和diff算法。

Vue2.x中的做法

在Vue2.x中,當數據改變后就要對所有的節點進行patch和diff操作。如以下DOM結構:

<div>
 <span class="header">I'm header</span>
 <ul>
  <li>第一個靜態li</li>
  <li v-for="item in mutableItems" :key="item.key"> {{ item.desc }}</li>
 </ul>
</div>

在第一次mount節點的時候會去生成真實的DOM,此后如果

mutableItems.push({
 key: 'asdf',
 desc: 'a new li item'
})

預期的結果是頁面出現新的一個li元素,內容就是 a new li item,Vue2.x中是通過patch時對 ul 元素對應的 vnode 的 children 來進行 diff 操作,具體操作在此不深究,但是該操作是需要比較所有的 li 對應的 vnode 的。

不足

正是由于2.x版本中的diff操作需要遍歷所有元素,本例中包括了 span 和 第一個li元素,但是這兩個元素是靜態的,不需要被比較的,不論數據怎么變,靜態元素都不會再更改了。vue-next在編譯時對這種操作做了優化,即 Block。

Block

入上述模板,在vue-next中生成的渲染函數為:

const _Vue = Vue
const { createVNode: _createVNode } = _Vue

const _hoisted_1 = _createVNode("span", { class: "header" }, "I'm header", -1 /* HOISTED */)
const _hoisted_2 = _createVNode("li", null, "第一個靜態li", -1 /* HOISTED */)

return function render(_ctx, _cache) {
 with (_ctx) {
  const { createVNode: _createVNode, renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, toDisplayString: _toDisplayString } = _Vue

  return (_openBlock(), _createBlock(_Fragment, null, [
   _hoisted_1,
   _createVNode("ul", null, [
    _hoisted_2,
    (_openBlock(true), _createBlock(_Fragment, null, _renderList(state.mutableItems, (item) => {
     return (_openBlock(), _createBlock("li", { key: item.key }, _toDisplayString(item.desc), 1 /* TEXT */))
    }), 128 /* KEYED_FRAGMENT */))
   ])
  ], 64 /* STABLE_FRAGMENT */))
 }
}

我們可以看到調用了 openBlock 和 createBlock 方法,這兩個方法的代碼實現也很簡單:

const blockStack: (VNode[] | null)[] = []
let currentBlock: VNode[] | null = null
let shouldTrack = 1
// openBlock
export function openBlock(disableTracking = false) {
 blockStack.push((currentBlock = disableTracking &#63; null : []))
}
export function createBlock(
 type: VNodeTypes | ClassComponent,
 props&#63;: { [key: string]: any } | null,
 children&#63;: any,
 patchFlag&#63;: number,
 dynamicProps&#63;: string[]
): VNode {
 // avoid a block with patchFlag tracking itself
 shouldTrack--
 const vnode = createVNode(type, props, children, patchFlag, dynamicProps)
 shouldTrack++
 // save current block children on the block vnode
 vnode.dynamicChildren = currentBlock || EMPTY_ARR
 // close block
 blockStack.pop()
 currentBlock = blockStack[blockStack.length - 1] || null
 // a block is always going to be patched, so track it as a child of its
 // parent block
 if (currentBlock) {
  currentBlock.push(vnode)
 }
 return vnode
}

更加詳細的注釋還請看源代碼中的注釋,寫的十分詳盡,便于理解。這里面 openBlock 就是初始化一個塊,createBlock 就是對當前編譯的內容生成一個塊,這里面的這一行代碼:vnode.dynamicChildren = currentBlock || EMPTY_ARR 就是在收集動態的子節點,我們可以再看一下編譯時運行的函數:

// createVNode
function _createVNode(
 type: VNodeTypes | ClassComponent,
 props: (Data & VNodeProps) | null = null,
 children: unknown = null,
 patchFlag: number = 0,
 dynamicProps: string[] | null = null
) {
 /**
  * 一系列代碼
 **/

 // presence of a patch flag indicates this node needs patching on updates.
 // component nodes also should always be patched, because even if the
 // component doesn't need to update, it needs to persist the instance on to
 // the next vnode so that it can be properly unmounted later.
 if (
  shouldTrack > 0 &&
  currentBlock &&
  // the EVENTS flag is only for hydration and if it is the only flag, the
  // vnode should not be considered dynamic due to handler caching.
  patchFlag !== PatchFlags.HYDRATE_EVENTS &&
  (patchFlag > 0 ||
   shapeFlag & ShapeFlags.SUSPENSE ||
   shapeFlag & ShapeFlags.STATEFUL_COMPONENT ||
   shapeFlag & ShapeFlags.FUNCTIONAL_COMPONENT)
 ) {
  currentBlock.push(vnode)
 }
}

上述函數是在模板編譯成ast之后調用的生成VNode的函數,所以有patchFlag這個標志,如果是動態的節點,并且此時是開啟了Block的話,就會將節點塞入Block中,這樣 createBlock返回的 VNode 中就會有 dynamicChildren 了。

到此為止,通過本文中案例經過模板編譯和render函數運行后并經過了優化以后生成了如下結構的vnode:

const result = {
 type: Symbol(Fragment),
 patchFlag: 64,
 children: [
  { type: 'span', patchFlag: -1, ...},
  {
   type: 'ul',
   patchFlag: 0,
   children: [
    { type: 'li', patchFlag: -1, ...},
    {
     type: Symbol(Fragment),
     children: [
      { type: 'li', patchFlag: 1 ...},
      { type: 'li', patchFlag: 1 ...}
     ]
    }
   ]
  }
 ],
 dynamicChildren: [
  {
   type: Symbol(Fragment),
   patchFlag: 128,
   children: [
    { type: 'li', patchFlag: 1 ...},
    { type: 'li', patchFlag: 1 ...}
   ]
  }
 ]
}

以上的 result 不完整,但是我們暫時只關心這些屬性。可以看見 result.children 的第一個元素是span,patchFlag=-1,且 result 有一個 dynamicChildren 數組,里面只包含了兩個動態的 li,后續如果變動了數據,那么新的 vnode.dynamicChildren 會有第三個 li 元素。

patch

patch部分其實也沒差多少,就是根據vnode的type執行不同的patch操作:

function patchElement(n1, n2) {
 let { dynamicChildren } = n2
 // 一系列操作

 if (dynamicChildren) {
  patchBlockChildren (
   n1.dynamicChildren!,
   dynamicChildren,
   el,
   parentComponent,
   parentSuspense,
   areChildrenSVG
  )
 } else if (!optimized) {
  // full diff
  patchChildren(
   n1,
   n2,
   el,
   null,
   parentComponent,
   parentSuspense,
   areChildrenSVG
  )
 }
}

可以看見,如果有了 dynamicChildren 那么vue2.x版本中的diff操作就被替換成了 patchBlockChildren() 且參數只有 dynamicChildren,就是靜態的不做diff操作了,而如果vue-next的patch中沒有 dynamicChildren,則進行完整的diff操作,入注釋寫的 full diff 的后續代碼。

以上就是關于Vue3中如何改進VDOM的內容,如果你們有學習到知識或者技能,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

彭阳县| 增城市| 开阳县| 子长县| 无极县| 杭锦后旗| 克什克腾旗| 蒙阴县| 邳州市| 喀喇沁旗| 托克托县| 团风县| 金山区| 金昌市| 会东县| 霍城县| 曲阜市| 西青区| 光泽县| 玛纳斯县| 禹州市| 新龙县| 密云县| 连平县| 北辰区| 朝阳区| 曲阳县| 崇州市| 万全县| 伊吾县| 德江县| 梁平县| 孟津县| 金坛市| 额敏县| 平果县| 荔浦县| 广宁县| 瓦房店市| 西和县| 来凤县|