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

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

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

背景

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

~200% faster creation of plain element VNodes

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

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

什么是 vnode

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

普通元素 vnode

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

<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é)點,在該示例中它是一個簡單的文本字符串,當(dāng)然,children 也可以是一個 vnode 數(shù)組。

組件 vnode

vnode 除了可以像上面那樣用于描述一個真實的 DOM,也可以用來描述組件。舉個例子,我們在模板中引入一個組件標(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 其實是對抽象事物的描述,這是因為我們并不會在頁面上真正渲染一個 <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è)計 vnode 這樣的數(shù)據(jù)結(jié)構(gòu)呢?

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

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

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

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

雖然 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 之前它的實現(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é)點
  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 是不是一個 vnode 節(jié)點:

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

判斷 type 是不是一個 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é)點,這個過程也會有一系列的判斷邏輯。

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

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

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

<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é)點,這里使用了 createElementVNode 方法而并非 createVNode 方法,而 createElementVNode 在內(nèi)部是 createBaseVNode 的別名,來看它的實現(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 的實現(xiàn),createBaseVNode 少執(zhí)行了很多判斷邏輯,自然性能就獲得了提升。

createVNode 的實現(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 的實現(xiàn)還是和之前類似,需要執(zhí)行一堆判斷邏輯,最終執(zhí)行 createBaseVNode 函數(shù)創(chuàng)建 vnode,注意這里 createBaseVNode 函數(shù)最后一個參數(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ōu)化,來實現(xiàn)整體的性能優(yōu)化。比如 Block Tree 的設(shè)計,就優(yōu)化了 diff 過程的性能。

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

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

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

相關(guān)文章

  • vue webpack開發(fā)訪問后臺接口全局配置的方法

    vue webpack開發(fā)訪問后臺接口全局配置的方法

    今天小編就為大家分享一篇vue webpack開發(fā)訪問后臺接口全局配置的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • vue自動添加瀏覽器兼容前后綴操作

    vue自動添加瀏覽器兼容前后綴操作

    這篇文章主要介紹了vue自動添加瀏覽器兼容前后綴操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • Vue3中使用qrcode庫實現(xiàn)二維碼生成

    Vue3中使用qrcode庫實現(xiàn)二維碼生成

    Vue3中實現(xiàn)二維碼生成需要使用第三方庫來處理生成二維碼的邏輯,常用的庫有?qrcode和?vue-qrcode,本文主要介紹了Vue3中使用qrcode庫實現(xiàn)二維碼生成,感興趣的可以了解一下
    2023-12-12
  • vant/vue跨域請求解決方案

    vant/vue跨域請求解決方案

    這篇文章主要介紹了vant/vue跨域請求解決方案,需要的朋友可以參考下
    2022-12-12
  • 一篇文章教會你部署vue項目到docker

    一篇文章教會你部署vue項目到docker

    在前端開發(fā)中,部署項目是我們經(jīng)常發(fā)生的事情,下面這篇文章主要給大家介紹了關(guān)于部署vue項目到docker的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • VUE安裝依賴時報錯:npm ERR! code ERESOLVE的解決

    VUE安裝依賴時報錯:npm ERR! code ERESOLVE的解決

    在使用npm安裝項目依賴時,有時會遇到錯誤信息 “npm ERR! code ERESOLVE”,本文就來介紹一下VUE安裝依賴時報錯的解決,具有一定的參考價值,感興趣的可以了解一下
    2023-10-10
  • 如何利用Vue3管理系統(tǒng)實現(xiàn)動態(tài)路由和動態(tài)側(cè)邊菜單欄

    如何利用Vue3管理系統(tǒng)實現(xiàn)動態(tài)路由和動態(tài)側(cè)邊菜單欄

    通常我們在vue項目中都是前端配置好路由的,但在一些項目中我們可能會遇到權(quán)限控制,這樣我們就涉及到動態(tài)路由的設(shè)置了,下面這篇文章主要給大家介紹了關(guān)于如何利用Vue3管理系統(tǒng)實現(xiàn)動態(tài)路由和動態(tài)側(cè)邊菜單欄的相關(guān)資料,需要的朋友可以參考下
    2022-02-02
  • vue之組件內(nèi)監(jiān)控$store中定義變量的變化詳解

    vue之組件內(nèi)監(jiān)控$store中定義變量的變化詳解

    今天小編就為大家分享一篇vue之組件內(nèi)監(jiān)控$store中定義變量的變化詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11
  • vue3-HOOKS模塊化處理方式

    vue3-HOOKS模塊化處理方式

    這篇文章主要介紹了vue3-HOOKS模塊化處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • element合并表格行的方法實現(xiàn)

    element合并表格行的方法實現(xiàn)

    本文主要介紹了element合并表格行的方法實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07

最新評論