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

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

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

背景

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

~200% faster creation of plain element VNodes

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

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

什么是 vnode

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

普通元素 vnode

什么是普通元素節(jié)點(diǎn)呢?舉個(gè)例子,在 HTML 中我們使用 <button> 標(biāo)簽來(lái)寫(xiě)一個(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)簽類(lèi)型;props 屬性表示 DOM 的一些附加信息,比如 styleclass 等;children 屬性表示 DOM 的子節(jié)點(diǎn),在該示例中它是一個(gè)簡(jiǎn)單的文本字符串,當(dāng)然,children 也可以是一個(gè) vnode 數(shù)組。

組件 vnode

vnode 除了可以像上面那樣用于描述一個(gè)真實(shí)的 DOM,也可以用來(lái)描述組件。舉個(gè)例子,我們?cè)谀0逯幸胍粋€(gè)組件標(biāo)簽 <custom-component>

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

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

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

組件 vnode 其實(shí)是對(duì)抽象事物的描述,這是因?yàn)槲覀儾⒉粫?huì)在頁(yè)面上真正渲染一個(gè) <custom-component> 標(biāo)簽,而最終會(huì)渲染組件內(nèi)部定義的 HTML 標(biāo)簽。

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

另外,Vue.js 3.x 內(nèi)部還針對(duì) vnodetype,做了更詳盡的分類(lèi),包括 Suspense、Teleport 等,并且把 vnode 的類(lèi)型信息做了編碼,以便在后面 vnode 的掛載階段,可以根據(jù)不同的類(lèi)型執(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)勢(shì)

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

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

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

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

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

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

如何創(chuàng)建 vnode

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

我們知道,組件模板經(jīng)過(guò)編譯,會(huì)生成對(duì)應(yīng)的 render 函數(shù),在 render 函數(shù)內(nèi)部,會(huì)執(zhí)行 createVNode 函數(shù)創(chuàng)建 vnode 對(duì)象,我們來(lái)看一下 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
  }
  // 類(lèi)組件的標(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 的類(lèi)型編碼
  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 的過(guò)程做了很多事情,其中有很多判斷的邏輯,比如判斷 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 類(lèi)型的組件:

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

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

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)
  }
}

接下來(lái)還會(huì)根據(jù) vnode 的類(lèi)型編碼:

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 對(duì)象,創(chuàng)建完后還會(huì)執(zhí)行 normalizeChildren 去標(biāo)準(zhǔn)化子節(jié)點(diǎn),這個(gè)過(guò)程也會(huì)有一系列的判斷邏輯。

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

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

順著這個(gè)思路,就可以在模板編譯階段,針對(duì)普通元素節(jié)點(diǎn),使用新的函數(shù)來(lái)創(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" })
    ])
  ]))
}

針對(duì)于 div 節(jié)點(diǎn),這里使用了 createElementVNode 方法而并非 createVNode 方法,而 createElementVNode 在內(nèi)部是 createBaseVNode 的別名,來(lái)看它的實(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)部?jī)H僅是創(chuàng)建了 vnode 對(duì)象,然后做了一些 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)還是和之前類(lèi)似,需要執(zhí)行一堆判斷邏輯,最終執(zhí)行 createBaseVNode 函數(shù)創(chuàng)建 vnode,注意這里 createBaseVNode 函數(shù)最后一個(gè)參數(shù)傳 true,也就是 needFullChildrenNormalizationtrue,那么在 createBaseVNode 的內(nèi)部,還需要多執(zhí)行 normalizeChildren 的邏輯。

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

總結(jié)

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

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

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

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

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

相關(guān)文章

  • vue webpack開(kāi)發(fā)訪(fǎng)問(wèn)后臺(tái)接口全局配置的方法

    vue webpack開(kāi)發(fā)訪(fǎng)問(wèn)后臺(tái)接口全局配置的方法

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

    vue自動(dòng)添加瀏覽器兼容前后綴操作

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

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

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

    vant/vue跨域請(qǐng)求解決方案

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

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

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

    VUE安裝依賴(lài)時(shí)報(bào)錯(cuò):npm ERR! code ERESOLVE的解決

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

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

    通常我們?cè)趘ue項(xiàng)目中都是前端配置好路由的,但在一些項(xiàng)目中我們可能會(huì)遇到權(quán)限控制,這樣我們就涉及到動(dòng)態(tài)路由的設(shè)置了,下面這篇文章主要給大家介紹了關(guān)于如何利用Vue3管理系統(tǒng)實(shí)現(xiàn)動(dòng)態(tài)路由和動(dòng)態(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中定義變量的變化詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-11-11
  • vue3-HOOKS模塊化處理方式

    vue3-HOOKS模塊化處理方式

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

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

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

最新評(píng)論