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

Vue.js3.2的vnode部分優(yōu)化升級使用示例詳解

 更新時(shí)間:2022年07月04日 09:16:47   作者:黃軼  
這篇文章主要為大家介紹了Vue.js3.2的vnode部分優(yōu)化升級使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

背景

上一篇文章,分析了 Vue.js 3.2 關(guān)于響應(yīng)式部分的優(yōu)化,此外,在這次優(yōu)化升級中,還有一個(gè)運(yùn)行時(shí)的優(yōu)化:

~200% faster creation of plain element VNodes

即針對普通元素類型 vnode 的創(chuàng)建,提升了約 200% 的性能。這也是一個(gè)非常偉大的優(yōu)化,是 Vue 的官方核心開發(fā)者 HcySunYang 實(shí)現(xiàn)的,可以參考這個(gè) PR。

那么具體是怎么做的呢,在分析實(shí)現(xiàn)前,我想先帶你了解一些 vnode 的背景知識。

什么是 vnode

vnode 本質(zhì)上是用來描述 DOM 的 JavaScript 對象,它在 Vue.js 中可以描述不同類型的節(jié)點(diǎn),比如普通元素節(jié)點(diǎn)、組件節(jié)點(diǎn)等。

普通元素 vnode

什么是普通元素節(jié)點(diǎn)呢?舉個(gè)例子,在 HTML 中我們使用 <button> 標(biāo)簽來寫一個(gè)按鈕:

<button class="btn" style="width:100px;height:50px">click me</button>

我們可以用 vnode 這樣表示 <button> 標(biāo)簽:

const vnode = {
  type: 'button',
  props: { 
    'class': 'btn',
    style: {
      width: '100px',
      height: '50px'
    }
  },
  children: 'click me'
}

其中,type 屬性表示 DOM 的標(biāo)簽類型;props 屬性表示 DOM 的一些附加信息,比如 style 、class 等;children 屬性表示 DOM 的子節(jié)點(diǎn),在該示例中它是一個(gè)簡單的文本字符串,當(dāng)然,children 也可以是一個(gè) vnode 數(shù)組。

組件 vnode

vnode 除了可以像上面那樣用于描述一個(gè)真實(shí)的 DOM,也可以用來描述組件。舉個(gè)例子,我們在模板中引入一個(gè)組件標(biāo)簽 <custom-component>

<custom-component msg="test"></custom-component>

我們可以用 vnode 這樣表示 <custom-component> 組件標(biāo)簽:

const CustomComponent = {
  // 在這里定義組件對象
}
const vnode = {
  type: CustomComponent,
  props: { 
    msg: 'test'
  }
}

組件 vnode 其實(shí)是對抽象事物的描述,這是因?yàn)槲覀儾⒉粫陧撁嫔险嬲秩疽粋€(gè) <custom-component> 標(biāo)簽,而最終會渲染組件內(nèi)部定義的 HTML 標(biāo)簽。

除了上述兩種 vnode 類型外,還有純文本 vnode、注釋 vnode 等等。

另外,Vue.js 3.x 內(nèi)部還針對 vnodetype,做了更詳盡的分類,包括 Suspense、Teleport 等,并且把 vnode 的類型信息做了編碼,以便在后面 vnode 的掛載階段,可以根據(jù)不同的類型執(zhí)行相應(yīng)的處理邏輯:

// runtime-core/src/vnode.ts
const shapeFlag = isString(type)
  ? 1 /* ELEMENT */
  : isSuspense(type)
    ? 128 /* SUSPENSE */
    : isTeleport(type)
      ? 64 /* TELEPORT */
      : isObject(type)
        ? 4 /* STATEFUL_COMPONENT */
        : isFunction(type)
          ? 2 /* FUNCTIONAL_COMPONENT */
          : 0;

vnode 的優(yōu)勢

知道什么是 vnode 后,你可能會好奇,那么 vnode 有什么優(yōu)勢呢?為什么一定要設(shè)計(jì) vnode 這樣的數(shù)據(jù)結(jié)構(gòu)呢?

首先是抽象,引入 vnode,可以把渲染過程抽象化,從而使得組件的抽象能力也得到提升。

其次是跨平臺,因?yàn)?patch vnode 的過程不同平臺可以有自己的實(shí)現(xiàn),基于 vnode 再做服務(wù)端渲染、weex 平臺、小程序平臺的渲染都變得容易了很多。

不過這里要特別注意,在瀏覽器端使用 vnode 并不意味著不用操作 DOM 了,很多人會誤以為 vnode 的性能一定比手動(dòng)操作原生 DOM 好,這個(gè)其實(shí)是不一定的。

因?yàn)檫@種基于 vnode 實(shí)現(xiàn)的 MVVM 框架,在每次組件渲染生成 vnode 的過程中,會有一定的 JavaScript 耗時(shí),尤其是是大組件。舉個(gè)例子,一個(gè) 1000 * 10 的 Table 組件,組件渲染生成 vnode 的過程會遍歷 1000 * 10 次去創(chuàng)建內(nèi)部 cell vnode,整個(gè)耗時(shí)就會變得比較長,再加上掛載 vnode 生成 DOM 的過程也會有一定的耗時(shí),當(dāng)我們?nèi)ジ陆M件的時(shí)候,用戶會感覺到明顯的卡頓。

雖然 diff 算法在減少 DOM 操作方面足夠優(yōu)秀,但最終還是免不了操作 DOM,所以說性能并不是 vnode 的優(yōu)勢。

如何創(chuàng)建 vnode

通常我們開發(fā)組件都是編寫組件的模板,并不會手寫 vnode,那么 vnode 是如何創(chuàng)建的呢?

我們知道,組件模板經(jīng)過編譯,會生成對應(yīng)的 render 函數(shù),在 render 函數(shù)內(nèi)部,會執(zhí)行 createVNode 函數(shù)創(chuàng)建 vnode 對象,我們來看一下 Vue.js 3.2 之前它的實(shí)現(xiàn):

function createVNode(type, props = null, children = null, patchFlag = 0, dynamicProps = null, isBlockNode = false) {
  if (!type || type === NULL_DYNAMIC_COMPONENT) {
    if ((process.env.NODE_ENV !== 'production') && !type) {
      warn(`Invalid vnode type when creating vnode: ${type}.`)
    }
    type = Comment
  }
  if (isVNode(type)) {
    const cloned = cloneVNode(type, props, true /* mergeRef: true */)
    if (children) {
      normalizeChildren(cloned, children)
    }
    return cloned
  }
  // 類組件的標(biāo)準(zhǔn)化
  if (isClassComponent(type)) {
    type = type.__vccOpts
  }
  // class 和 style 標(biāo)準(zhǔn)化.
  if (props) {
    if (isProxy(props) || InternalObjectKey in props) {
      props = extend({}, props)
    }
    let { class: klass, style } = props
    if (klass && !isString(klass)) {
      props.class = normalizeClass(klass)
    }
    if (isObject(style)) {
      if (isProxy(style) && !isArray(style)) {
        style = extend({}, style)
      }
      props.style = normalizeStyle(style)
    }
  }
  // 根據(jù) vnode 的類型編碼
  const shapeFlag = isString(type)
    ? 1 /* ELEMENT */
    : isSuspense(type)
      ? 128 /* SUSPENSE */
      : isTeleport(type)
        ? 64 /* TELEPORT */
        : isObject(type)
          ? 4 /* STATEFUL_COMPONENT */
          : isFunction(type)
            ? 2 /* FUNCTIONAL_COMPONENT */
            : 0
  if ((process.env.NODE_ENV !== 'production') && shapeFlag & 4 /* STATEFUL_COMPONENT */ && isProxy(type)) {
    type = toRaw(type)
    warn(`Vue received a Component which was made a reactive object. This can ` +
      `lead to unnecessary performance overhead, and should be avoided by ` +
      `marking the component with `markRaw` or using `shallowRef` ` +
      `instead of `ref`.`, `\nComponent that was made reactive: `, type)
  }
  const vnode = {
    __v_isVNode: true,
    __v_skip: true,
    type,
    props,
    key: props && normalizeKey(props),
    ref: props && normalizeRef(props),
    scopeId: currentScopeId,
    slotScopeIds: null,
    children: null,
    component: null,
    suspense: null,
    ssContent: null,
    ssFallback: null,
    dirs: null,
    transition: null,
    el: null,
    anchor: null,
    target: null,
    targetAnchor: null,
    staticCount: 0,
    shapeFlag,
    patchFlag,
    dynamicProps,
    dynamicChildren: null,
    appContext: null
  }
  if ((process.env.NODE_ENV !== 'production') && vnode.key !== vnode.key) {
    warn(`VNode created with invalid key (NaN). VNode type:`, vnode.type)
  }
  normalizeChildren(vnode, children)
  // 標(biāo)準(zhǔn)化 suspense 子節(jié)點(diǎn)
  if (shapeFlag & 128 /* SUSPENSE */) {
    type.normalize(vnode)
  }
  if (isBlockTreeEnabled > 0 &&
    !isBlockNode &&
    currentBlock &&
    (patchFlag > 0 || shapeFlag & 6 /* COMPONENT */) &&
    patchFlag !== 32 /* HYDRATE_EVENTS */) {
    currentBlock.push(vnode)
  }
  return vnode
}

可以看到,創(chuàng)建 vnode 的過程做了很多事情,其中有很多判斷的邏輯,比如判斷 type 是否為空:

if (!type || type === NULL_DYNAMIC_COMPONENT) {
  if ((process.env.NODE_ENV !== 'production') && !type) {
    warn(`Invalid vnode type when creating vnode: ${type}.`)
  }
  type = Comment
}

判斷 type 是不是一個(gè) vnode 節(jié)點(diǎn):

if (isVNode(type)) {
  const cloned = cloneVNode(type, props, true /* mergeRef: true */)
  if (children) {
    normalizeChildren(cloned, children)
  }
  return cloned
}

判斷 type 是不是一個(gè) class 類型的組件:

if (isClassComponent(type)) {
    type = type.__vccOpts
  }

除此之外,還會對屬性中的 styleclass 執(zhí)行標(biāo)準(zhǔn)化,其中也會有一些判斷邏輯:

if (props) {
  if (isProxy(props) || InternalObjectKey in props) {
    props = extend({}, props)
  }
  let { class: klass, style } = props
  if (klass && !isString(klass)) {
    props.class = normalizeClass(klass)
  }
  if (isObject(style)) {
    if (isProxy(style) && !isArray(style)) {
      style = extend({}, style)
    }
    props.style = normalizeStyle(style)
  }
}

接下來還會根據(jù) vnode 的類型編碼:

const shapeFlag = isString(type)
  ? 1 /* ELEMENT */
  : isSuspense(type)
    ? 128 /* SUSPENSE */
    : isTeleport(type)
      ? 64 /* TELEPORT */
      : isObject(type)
        ? 4 /* STATEFUL_COMPONENT */
        : isFunction(type)
          ? 2 /* FUNCTIONAL_COMPONENT */
          : 0

然后就是創(chuàng)建 vnode 對象,創(chuàng)建完后還會執(zhí)行 normalizeChildren 去標(biāo)準(zhǔn)化子節(jié)點(diǎn),這個(gè)過程也會有一系列的判斷邏輯。

創(chuàng)建 vnode 過程的優(yōu)化

仔細(xì)想想,vnode 本質(zhì)上就是一個(gè) JavaScript 對象,之所以在創(chuàng)建過程中做很多判斷,是因?yàn)橐幚砀鞣N各樣的情況。然而對于普通元素 vnode 而言,完全不需要這么多的判斷邏輯,因此對于普通元素 vnode,使用 createVNode 函數(shù)創(chuàng)建就是一種浪費(fèi)。

順著這個(gè)思路,就可以在模板編譯階段,針對普通元素節(jié)點(diǎn),使用新的函數(shù)來創(chuàng)建 vnode,Vue.js 3.2 就是這么做的,舉個(gè)例子:

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

借助于模板導(dǎo)出工具,可以看到它編譯后的 render 函數(shù):

import { createElementVNode as _createElementVNode, resolveComponent as _resolveComponent, createVNode as _createVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
const _hoisted_1 = { class: "home" }
const _hoisted_2 = /*#__PURE__*/_createElementVNode("img", {
  alt: "Vue logo",
  src: "../assets/logo.png"
}, null, -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
  const _component_HelloWorld = _resolveComponent("HelloWorld")
  return (_openBlock(), _createElementBlock("template", null, [
    _createElementVNode("div", _hoisted_1, [
      _hoisted_2,
      _createVNode(_component_HelloWorld, { msg: "Welcome to Your Vue.js App" })
    ])
  ]))
}

針對于 div 節(jié)點(diǎn),這里使用了 createElementVNode 方法而并非 createVNode 方法,而 createElementVNode 在內(nèi)部是 createBaseVNode 的別名,來看它的實(shí)現(xiàn):

function createBaseVNode(type, props = null, children = null, patchFlag = 0, dynamicProps = null, shapeFlag = type === Fragment ? 0 : 1 /* ELEMENT */, isBlockNode = false, needFullChildrenNormalization = false) {
  const vnode = {
    __v_isVNode: true,
    __v_skip: true,
    type,
    props,
    key: props && normalizeKey(props),
    ref: props && normalizeRef(props),
    scopeId: currentScopeId,
    slotScopeIds: null,
    children,
    component: null,
    suspense: null,
    ssContent: null,
    ssFallback: null,
    dirs: null,
    transition: null,
    el: null,
    anchor: null,
    target: null,
    targetAnchor: null,
    staticCount: 0,
    shapeFlag,
    patchFlag,
    dynamicProps,
    dynamicChildren: null,
    appContext: null
  }
  if (needFullChildrenNormalization) {
    normalizeChildren(vnode, children)
    if (shapeFlag & 128 /* SUSPENSE */) {
      type.normalize(vnode)
    }
  }
  else if (children) {
    vnode.shapeFlag |= isString(children)
      ? 8 /* TEXT_CHILDREN */
      : 16 /* ARRAY_CHILDREN */
  }
  if ((process.env.NODE_ENV !== 'production') && vnode.key !== vnode.key) {
    warn(`VNode created with invalid key (NaN). VNode type:`, vnode.type)
  }
  if (isBlockTreeEnabled > 0 &&
    !isBlockNode &&
    currentBlock &&
    (vnode.patchFlag > 0 || shapeFlag & 6 /* COMPONENT */) &&
    vnode.patchFlag !== 32 /* HYDRATE_EVENTS */) {
    currentBlock.push(vnode)
  }
  return vnode
}

可以看到,createBaseVNode 內(nèi)部僅僅是創(chuàng)建了 vnode 對象,然后做了一些 block 邏輯的處理。相比于之前的 createVNode 的實(shí)現(xiàn),createBaseVNode 少執(zhí)行了很多判斷邏輯,自然性能就獲得了提升。

createVNode 的實(shí)現(xiàn),是基于 createBaseVNode 做的一層封裝:

function createVNode(type, props = null, children = null, patchFlag = 0, dynamicProps = null, isBlockNode = false) {
  if (!type || type === NULL_DYNAMIC_COMPONENT) {
    if ((process.env.NODE_ENV !== 'production') && !type) {
      warn(`Invalid vnode type when creating vnode: ${type}.`)
    }
    type = Comment$1
  }
  if (isVNode(type)) {
    const cloned = cloneVNode(type, props, true /* mergeRef: true */)
    if (children) {
      normalizeChildren(cloned, children)
    }
    return cloned
  }
  if (isClassComponent(type)) {
    type = type.__vccOpts
  }
  if (props) {
    props = guardReactiveProps(props)
    let { class: klass, style } = props
    if (klass && !isString(klass)) {
      props.class = normalizeClass(klass)
    }
    if (isObject$1(style)) {
      if (isProxy(style) && !isArray(style)) {
        style = extend({}, style)
      }
      props.style = normalizeStyle(style)
    }
  }
  const shapeFlag = isString(type)
    ? 1 /* ELEMENT */
    : isSuspense(type)
      ? 128 /* SUSPENSE */
      : isTeleport(type)
        ? 64 /* TELEPORT */
        : isObject$1(type)
          ? 4 /* STATEFUL_COMPONENT */
          : isFunction$1(type)
            ? 2 /* FUNCTIONAL_COMPONENT */
            : 0
  if ((process.env.NODE_ENV !== 'production') && shapeFlag & 4 /* STATEFUL_COMPONENT */ && isProxy(type)) {
    type = toRaw(type)
    warn(`Vue received a Component which was made a reactive object. This can ` +
      `lead to unnecessary performance overhead, and should be avoided by ` +
      `marking the component with `markRaw` or using `shallowRef` ` +
      `instead of `ref`.`, `\nComponent that was made reactive: `, type)
  }
  return createBaseVNode(type, props, children, patchFlag, dynamicProps, shapeFlag, isBlockNode, true)
}

createVNode 的實(shí)現(xiàn)還是和之前類似,需要執(zhí)行一堆判斷邏輯,最終執(zhí)行 createBaseVNode 函數(shù)創(chuàng)建 vnode,注意這里 createBaseVNode 函數(shù)最后一個(gè)參數(shù)傳 true,也就是 needFullChildrenNormalizationtrue,那么在 createBaseVNode 的內(nèi)部,還需要多執(zhí)行 normalizeChildren 的邏輯。

組件 vnode 還是通過 createVNode 函數(shù)來創(chuàng)建。

總結(jié)

雖然看上去只是少執(zhí)行了幾行代碼,但由于大部分頁面都是由很多普通 DOM 元素構(gòu)成,創(chuàng)建普通元素 vnode 過程的優(yōu)化,對整體頁面的渲染和更新都會有很大的性能提升。

由于存在模板編譯的過程,Vue.js 可以利用編譯 + 運(yùn)行時(shí)優(yōu)化,來實(shí)現(xiàn)整體的性能優(yōu)化。比如 Block Tree 的設(shè)計(jì),就優(yōu)化了 diff 過程的性能。

其實(shí)對一個(gè)框架越了解,你就會越有敬畏之情,Vue.js 在編譯、運(yùn)行時(shí)的實(shí)現(xiàn)都下了非常大的功夫,處理的細(xì)節(jié)很多,因此代碼的體積也難免變大。而且在框架已經(jīng)足夠成熟,有大量用戶使用的背景下還能從內(nèi)部做這么多的性能優(yōu)化,并且保證沒有 regression bug,實(shí)屬不易。

開源作品的用戶越多,受到的挑戰(zhàn)也會越大,需要考慮的細(xì)節(jié)就會越多,如果一個(gè)開源作品都沒啥人用,玩具級別,就真的別來碰瓷 Vue 了,根本不是一個(gè)段位的。

以上就是Vue.js3.2的vnode部分優(yōu)化升級使用示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Vue.js vnode優(yōu)化升級的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • axios向后臺傳遞數(shù)組作為參數(shù)的方法

    axios向后臺傳遞數(shù)組作為參數(shù)的方法

    今天小編就為大家分享一篇axios向后臺傳遞數(shù)組作為參數(shù)的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • Vue使用枚舉類型實(shí)現(xiàn)HTML下拉框步驟詳解

    Vue使用枚舉類型實(shí)現(xiàn)HTML下拉框步驟詳解

    本文分步驟給大家介紹了Vue使用枚舉類型實(shí)現(xiàn)HTML下拉框的相關(guān)知識,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02
  • Vue3之事件綁定的實(shí)現(xiàn)邏輯詳解

    Vue3之事件綁定的實(shí)現(xiàn)邏輯詳解

    這篇文章主要介紹了Vue3之事件綁定的實(shí)現(xiàn)邏輯,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • vue3項(xiàng)目如何使用樣式穿透修改elementUI默認(rèn)樣式

    vue3項(xiàng)目如何使用樣式穿透修改elementUI默認(rèn)樣式

    這篇文章主要介紹了vue3項(xiàng)目使用樣式穿透修改elementUI默認(rèn)樣式,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • 前端vue-cli項(xiàng)目中使用img圖片和background背景圖的幾種方法

    前端vue-cli項(xiàng)目中使用img圖片和background背景圖的幾種方法

    這篇文章主要介紹了前端vue-cli項(xiàng)目中使用img圖片和background背景圖的幾種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • Vue學(xué)習(xí)之組件用法實(shí)例詳解

    Vue學(xué)習(xí)之組件用法實(shí)例詳解

    這篇文章主要介紹了Vue學(xué)習(xí)之組件用法,結(jié)合實(shí)例形式分析了vue.js組件的使用流程、模板、父子組件、插槽slot等相關(guān)原理與操作技巧,需要的朋友可以參考下
    2020-01-01
  • vue+highcharts實(shí)現(xiàn)3D餅圖效果

    vue+highcharts實(shí)現(xiàn)3D餅圖效果

    這篇文章主要為大家詳細(xì)介紹了vue+highcharts實(shí)現(xiàn)3D餅圖效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 詳解?Vue虛擬DOM如何提高前端開發(fā)效率

    詳解?Vue虛擬DOM如何提高前端開發(fā)效率

    這篇文章主要為大家介紹了詳解?Vue虛擬DOM如何提高前端開發(fā)效率,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • 快速解決electron-builder打包時(shí)下載依賴慢的問題

    快速解決electron-builder打包時(shí)下載依賴慢的問題

    使用 Electron-builder 打包,有時(shí)會在下載Electron、nsis、winCodeSign的過程中 Timeout 導(dǎo)致打包失敗,本文給大家分享快速解決electron-builder打包時(shí)下載依賴慢的問題,感興趣的朋友一起看看吧
    2024-02-02
  • 某些場景下建議vue query代替pinia原理解析

    某些場景下建議vue query代替pinia原理解析

    這篇文章主要為大家介紹了某些場景下建議vue-query代替pinia原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02

最新評論

曲松县| 谢通门县| 沁阳市| 黔江区| 南岸区| 新竹市| 长汀县| 文水县| 江川县| 海南省| 翁牛特旗| 小金县| 三穗县| 宽城| 南投市| 乡城县| 绿春县| 进贤县| 沁水县| 彰化县| 宁津县| 通化市| 米泉市| 重庆市| 灵丘县| 贞丰县| 阿城市| 若羌县| 潢川县| 保山市| 教育| 凭祥市| 正镶白旗| 略阳县| 金寨县| 彝良县| 桦甸市| 福清市| 理塘县| 吉隆县| 个旧市|