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

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

 更新時間: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)化升級中,還有一個運(yùn)行時的優(yōu)化:

~200% faster creation of plain element VNodes

即針對普通元素類型 vnode 的創(chuàng)建,提升了約 200% 的性能。這也是一個非常偉大的優(yōu)化,是 Vue 的官方核心開發(fā)者 HcySunYang 實(shí)現(xiàn)的,可以參考這個 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)呢?舉個例子,在 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é)點(diǎn),在該示例中它是一個簡單的文本字符串,當(dāng)然,children 也可以是一個 vnode 數(shù)組。

組件 vnode

vnode 除了可以像上面那樣用于描述一個真實(shí)的 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 其實(shí)是對抽象事物的描述,這是因?yàn)槲覀儾⒉粫陧撁嫔险嬲秩疽粋€ <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,可以把渲染過程抽象化,從而使得組件的抽象能力也得到提升。

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

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

因?yàn)檫@種基于 vnode 實(shí)現(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 之前它的實(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 是不是一個 vnode 節(jié)點(diǎn):

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

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

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

順著這個思路,就可以在模板編譯階段,針對普通元素節(jié)點(diǎn),使用新的函數(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é)點(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ù)最后一個參數(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)行時優(yōu)化,來實(shí)現(xiàn)整體的性能優(yōu)化。比如 Block Tree 的設(shè)計,就優(yōu)化了 diff 過程的性能。

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

開源作品的用戶越多,受到的挑戰(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庫實(shí)現(xiàn)二維碼生成

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

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

    vant/vue跨域請求解決方案

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

    一篇文章教會你部署vue項(xiàng)目到docker

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

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

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

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

    通常我們在vue項(xiàng)目中都是前端配置好路由的,但在一些項(xiàng)目中我們可能會遇到權(quán)限控制,這樣我們就涉及到動態(tài)路由的設(shè)置了,下面這篇文章主要給大家介紹了關(guān)于如何利用Vue3管理系統(tǒng)實(shí)現(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合并表格行的方法實(shí)現(xiàn)

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

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

最新評論