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

Cookbook組件形式:優(yōu)化 Vue 組件的運(yùn)行時(shí)性能

 更新時(shí)間:2018年11月25日 11:02:03   作者:前端小透明  
本文仿照Vue Cookbook 組織形式,對優(yōu)化 Vue 組件的運(yùn)行時(shí)性能進(jìn)行闡述。通過基本的示例代碼給大家講解,需要的朋友參考下

Vue 2.0 在發(fā)布之初,就以其優(yōu)秀的運(yùn)行時(shí)性能著稱,你可以通過這個(gè)第三方 benchmark 來對比其他框架的性能。Vue 使用了 Virtual DOM 來進(jìn)行視圖渲染,當(dāng)數(shù)據(jù)變化時(shí),Vue 會對比前后兩棵組件樹,只將必要的更新同步到視圖上。

Vue 幫我們做了很多,但對于一些復(fù)雜場景,特別是大量的數(shù)據(jù)渲染,我們應(yīng)當(dāng)時(shí)刻關(guān)注應(yīng)用的運(yùn)行時(shí)性能。

本文仿照Vue Cookbook 組織形式,對優(yōu)化 Vue 組件的運(yùn)行時(shí)性能進(jìn)行闡述。

基本的示例

在下面的示例中,我們開發(fā)了一個(gè)樹形控件,支持基本的樹形結(jié)構(gòu)展示以及節(jié)點(diǎn)的展開與折疊。

我們定義 Tree 組件的接口如下。 data 綁定了樹形控件的數(shù)據(jù),是若干顆樹組成的數(shù)組, children 表示子節(jié)點(diǎn)。 expanded-keys 綁定了展開的節(jié)點(diǎn)的 key 屬性,使用 sync 修飾符來同步組件內(nèi)部觸發(fā)的節(jié)點(diǎn)展開狀態(tài)的更新。

<template>
 <tree :data="data" expanded-keys.sync="expandedKeys"></tree>
</template>

<script>
export default {
 data() {
 return {
 data: [{
 key: '1',
 label: '節(jié)點(diǎn) 1',
 children: [{
  key: '1-1',
  label: '節(jié)點(diǎn) 1-1'
 }]
 }, {
 key: '2',
 label: '節(jié)點(diǎn) 2'
 }]
 }
 }
};
</script>

Tree 組件的實(shí)現(xiàn)如下,這是個(gè)稍微復(fù)雜的例子,需要花幾分鐘時(shí)間閱讀一下。

<template>
 <ul class="tree">
 <li
 v-for="node in nodes"
 v-show="status[node.key].visible"
 :key="node.key"
 class="tree-node"
 :style="{ 'padding-left': `${node.level * 16}px` }"
 >
 <i
 v-if="node.children"
 class="tree-node-arrow"
 :class="{ expanded: status[node.key].expanded }"
 @click="changeExpanded(node.key)"
 >
 </i>
 {{ node.label }}
 </li>
 </ul>
</template>

<script>
export default {
 props: {
 data: Array,
 expandedKeys: {
 type: Array,
 default: () => [],
 },
 },
 computed: {
 // 將 data 轉(zhuǎn)為一維數(shù)組,方便 v-for 進(jìn)行遍歷
 // 同時(shí)添加 level 和 parent 屬性
 nodes() {
 return this.getNodes(this.data);
 },
 // status 是一個(gè) key 和節(jié)點(diǎn)狀態(tài)的一個(gè) Map 數(shù)據(jù)結(jié)構(gòu)
 status() {
 return this.getStatus(this.nodes);
 },
 },
 methods: {
 // 對 data 進(jìn)行遞歸,返回一個(gè)所有節(jié)點(diǎn)的一維數(shù)組
 getNodes(data, level = 0, parent = null) {
 let nodes = [];
 data.forEach((item) => {
 const node = {
  level,
  parent,
  ...item,
 };
 nodes.push(node);
 if (item.children) {
  const children = this.getNodes(item.children, level + 1, node);
  nodes = [...nodes, ...children];
  node.children = children.filter(child => child.level === level + 1);
 }
 });
 return nodes;
 },
 // 遍歷 nodes,計(jì)算每個(gè)節(jié)點(diǎn)的狀態(tài)
 getStatus(nodes) {
 const status = {};
 nodes.forEach((node) => {
 const parentStatus = status[node.parent && node.parent.key] || {};
 status[node.key] = {
  expanded: this.expandedKeys.includes(node.key),
  visible: node.level === 0 || (parentStatus.expanded && parentStatus.visible),
 };
 });
 return status;
 },
 // 切換節(jié)點(diǎn)的展開狀態(tài)
 changeExpanded(key) {
 const index = this.expandedKeys.indexOf(key);
 const expandedKeys = [...this.expandedKeys];
 if (index >= 0) {
 expandedKeys.splice(index, 1);
 } else {
 expandedKeys.push(key);
 }
 this.$emit('update:expandedKeys', expandedKeys);
 },
 },
};
</script>

展開或折疊節(jié)點(diǎn)時(shí),我們只需更新 expanded-keysstatus 計(jì)算屬性便會自動更新,保證關(guān)聯(lián)子節(jié)點(diǎn)可見狀態(tài)的正確。

一切準(zhǔn)備就緒,為了度量 Tree 組件的運(yùn)行性能,我們設(shè)定了兩個(gè)指標(biāo)。

初次渲染時(shí)間 節(jié)點(diǎn)展開 / 折疊時(shí)間

在 Tree 組件中添加代碼如下,使用 console.timeconsole.timeEnd 可以輸出某個(gè)操作的具體耗時(shí)。

export default {
 // ...
 methods: {
 // ...
 changeExpanded(key) {
 // ...
 this.$emit('update:expandedKeys', expandedKeys);

 console.time('expanded change');

 this.$nextTick(() => {
 console.timeEnd('expanded change');
 });
 },
 },
 beforeCreate() {
 console.time('first rendering');
 },
 mounted() {
 console.timeEnd('first rendering');
 },
};

同時(shí),為了放大可能存在的性能問題,我們編寫了一個(gè)方法來生成可控?cái)?shù)量的節(jié)點(diǎn)數(shù)據(jù)。

<template>
 <tree :data="data" :expanded-keys.sync="expandedKeys"></tree>
</template>

<script>
export default {
 data() {
 return {
 // 生成一個(gè)有 3 層,每層 10 個(gè)共 1000 個(gè)節(jié)點(diǎn)的節(jié)點(diǎn)樹
 data: this.getRandomData(3, 10),
 expandedKeys: [],
 };
 },
 methods: {
 getRandomData(layers, count, parent) {
 return Array.from({ length: count }, (v, i) => {
 const key = (parent ? `${parent.key}-` : '') + (i + 1);
 const node = {
  key,
  label: `節(jié)點(diǎn) ${key}`,
 };
 if (layers > 1) {
  node.children = this.getRandomData(layers - 1, count, node);
 }
 return node;
 });
 },
 },
};
<script>

你可以通過這個(gè)CodeSandbox 完整示例來實(shí)際觀察下性能損耗。點(diǎn)擊箭頭展開或折疊某個(gè)節(jié)點(diǎn),在 Chrome DevTools 的控制臺(不要使用 CodeSandbox 的控制臺,不準(zhǔn)確)中輸出如下。

first rendering: 406.068115234375ms
expanded change: 231.623779296875ms

在筆者的低功耗筆記本下,初次渲染耗時(shí) 400+ms,展開或折疊節(jié)點(diǎn) 200+ms。下面我們來優(yōu)化 Tree 組件的運(yùn)行性能。

若你的設(shè)備性能強(qiáng)勁,可修改生成的節(jié)點(diǎn)數(shù)量,如 this.getRandomData(4, 10) 生成 10000 個(gè)節(jié)點(diǎn)。

使用 Chrome Performance 查找性能瓶頸

Chrome 的 Performance 面板可以錄制一段時(shí)間內(nèi)的 js 執(zhí)行細(xì)節(jié)及時(shí)間。使用 Chrome 開發(fā)者工具分析頁面性能的步驟如下。

打開 Chrome 開發(fā)者工具,切換到 Performance 面板 點(diǎn)擊 Record 開始錄制 刷新頁面或展開某個(gè)節(jié)點(diǎn) 點(diǎn)擊 Stop 停止錄制

console.time 輸出的值也會顯示在 Performance 中,幫助我們調(diào)試。更多關(guān)于 Performance 的內(nèi)容可以點(diǎn)擊這里查看。

優(yōu)化運(yùn)行時(shí)性能

條件渲染

我們往下翻閱 Performance 分析結(jié)果,發(fā)現(xiàn)大部分耗時(shí)都在 render 函數(shù)上,并且下面還有很多其他函數(shù)的調(diào)用。

在遍歷節(jié)點(diǎn)時(shí),對于節(jié)點(diǎn)的可見性我們使用的是 v-show 指令,不可見的節(jié)點(diǎn)也會渲染出來,然后通過樣式使其不可見。因此嘗試使用 v-if 指令來進(jìn)行條件渲染。

<li
 v-for="node in nodes"
 v-if="status[node.key].visible"
 :key="node.key"
 class="tree-node"
 :style="{ 'padding-left': `${node.level * 16}px` }"
>
 ...
</li>

v-if 在 render 函數(shù)中表現(xiàn)為一個(gè)三目表達(dá)式:

visible ? h('li') : this._e() // this._e() 生成一個(gè)注釋節(jié)點(diǎn)

v-if 只是減少每次遍歷的時(shí)間,并不能減少遍歷的次數(shù)。且Vue.js 風(fēng)格指南中明確指出不要把 v-ifv-for 同時(shí)用在同一個(gè)元素上,因?yàn)檫@可能會導(dǎo)致不必要的渲染。

我們可以更換為在一個(gè)可見節(jié)點(diǎn)的計(jì)算屬性上進(jìn)行遍歷:

<li
 v-for="node in visibleNodes"
 :key="node.key"
 class="tree-node"
 :style="{ 'padding-left': `${node.level * 16}px` }"
>
 ...
</li>

<script>
export {
 // ...
 computed: {
 visibleNodes() {
 return this.nodes.filter(node => this.status[node.key].visible);
 },
 },
 // ...
}
</script>

優(yōu)化后的性能耗時(shí)如下:

first rendering: 194.7890625ms
expanded change: 204.01904296875ms

你可以通過改進(jìn)后的示例 (Demo2) 來觀察組件的性能損耗,相比優(yōu)化前有很大的提升。

雙向綁定

在前面的示例中,我們使用 .syncexpanded-keys 進(jìn)行了“雙向綁定”,其實(shí)際上是 prop 和自定義事件的語法糖。這種方式能很方便地讓 Tree 的父組件同步展開狀態(tài)的更新。

但是,使用 Tree 組件時(shí),不傳 expanded-keys ,會導(dǎo)致節(jié)點(diǎn)無法展開或折疊,即使你不關(guān)心展開或折疊的操作。這里把 expanded-keys 作為外界的副作用了。

<!-- 無法展開 / 折疊節(jié)點(diǎn) -->
<tree :data="data"></tree>

這里還存在一些性能問題,展開或折疊某一節(jié)點(diǎn)時(shí),觸發(fā)父組件的副作用更新 expanded-keys 。Tree 組件的 status 依賴了 expanded-keys ,會調(diào)用 this.getStatus 方法獲取新的 status 。即使只是單個(gè)節(jié)點(diǎn)的狀態(tài)改變,也會導(dǎo)致重新計(jì)算所有節(jié)點(diǎn)的狀態(tài)。

我們考慮將 status 作為一個(gè) Tree 組件的內(nèi)部狀態(tài),展開或折疊某個(gè)節(jié)點(diǎn)時(shí),直接對 status 進(jìn)行修改。同時(shí)定義默認(rèn)的展開節(jié)點(diǎn) default-expanded-keys 。 status 只在初始化時(shí)依賴 default-expanded-keys

export default {
 props: {
 data: Array,
 // 默認(rèn)展開節(jié)點(diǎn)
 defaultExpandedKeys: {
 type: Array,
 default: () => [],
 },
 },
 data() {
 return {
 status: null, // status 為局部狀態(tài)
 };
 },
 computed: {
 nodes() {
 return this.getNodes(this.data);
 },
 },
 watch: {
 nodes: {
 // nodes 改變時(shí)重新計(jì)算 status
 handler() {
 this.status = this.getStatus(this.nodes);
 },
 // 初始化 status
 immediate: true,
 },
 // defaultExpandedKeys 改變時(shí)重新計(jì)算 status
 defaultExpandedKeys() {
 this.status = this.getStatus(this.nodes);
 },
 },
 methods: {
 getNodes(data, level = 0, parent = null) {
 // ...
 },
 getStatus(nodes) {
 // ...
 },
 // 展開或折疊節(jié)點(diǎn)時(shí)直接修改 status,并通知父組件
 changeExpanded(key) {
 console.time('expanded change');

 const node = this.nodes.find(n => n.key === key); // 找到該節(jié)點(diǎn)
 const newExpanded = !this.status[key].expanded; // 新的展開狀態(tài)
 
 // 遞歸該節(jié)點(diǎn)的后代節(jié)點(diǎn),更新 status
 const updateVisible = (n, visible) => {
 n.children.forEach((child) => {
  this.status[child.key].visible = visible && this.status[n.key].expanded;
  if (child.children) updateVisible(child, visible);
 });
 };

 this.status[key].expanded = newExpanded;

 updateVisible(node, newExpanded);

 // 觸發(fā)節(jié)點(diǎn)展開狀態(tài)改變事件
 this.$emit('expanded-change', node, newExpanded, this.nodes.filter(n => this.status[n.key].expanded));

 this.$nextTick(() => {
 console.timeEnd('expanded change');
 });
 },
 },
 beforeCreate() {
 console.time('first rendering');
 },
 mounted() {
 console.timeEnd('first rendering');
 },
};

使用 Tree 組件時(shí),即使不傳 default-expanded-keys ,節(jié)點(diǎn)也能正常地展開或收起。

<!-- 節(jié)點(diǎn)可以展開或收起 -->
<tree :data="data"></tree>

<!-- 配置默認(rèn)展開的節(jié)點(diǎn) -->
<tree
 :data="data"
 :default-expanded-keys="['1', '1-1']"
 @expanded-change="handleExpandedChange"
>
</tree>

優(yōu)化后的性能耗時(shí)如下。

first rendering: 91.48193359375ms
expanded change: 20.4287109375ms

你可以通過改進(jìn)后的示例 (Demo3) 來觀察組件的性能損耗。

凍結(jié)數(shù)據(jù)

到此為止,Tree 組件的性能問題已經(jīng)不是很明顯了。為了進(jìn)一步擴(kuò)大性能問題,查找優(yōu)化空間。我們把節(jié)點(diǎn)數(shù)量增加到 10000 個(gè)。

// 生成 10000 個(gè)節(jié)點(diǎn)
this.getRandomData(4, 1000)

這里,我們故意制造一個(gè)可能存在性能問題的改動。雖然這不是必須的,當(dāng)它能幫助我們了解接下來所要介紹的問題。

將計(jì)算屬性 nodes 修改為在 datawatcher 中去獲取 nodes 的值。

export default {
 // ...
 watch: {
 data: {
 handler() {
 this.nodes = this.getNodes(this.data);
 this.status = this.getStatus(this.nodes);
 },
 immediate: true,
 },
 // ...
 },
 // ...
};

這種修改對于實(shí)現(xiàn)的功能是沒有影響的,那么性能情況如何呢。

first rendering: 490.119140625ms
expanded change: 183.94189453125ms

使用 Performance 工具嘗試查找性能瓶頸。

我們發(fā)現(xiàn),在 getNodes 方法調(diào)用之后,有一段耗時(shí)很長的 proxySetter 。這是 Vue 在為 nodes 屬性添加響應(yīng)式,讓 Vue 能夠追蹤依賴的變化。 getStatus 同理。

當(dāng)你把一個(gè)普通的 JavaScript 對象傳給 Vue 實(shí)例的 data 選項(xiàng),Vue 將遍歷此對象所有的屬性,并使用 Object.defineProperty 把這些屬性全部轉(zhuǎn)為 getter/setter。

對象越復(fù)雜,層級越深,這個(gè)過程消耗的時(shí)間越長。當(dāng)我們存在 1w 個(gè)節(jié)點(diǎn)時(shí), proxySetter 的時(shí)間就會非常長了。

這里存在一個(gè)問題,我們不會對 nodes 某個(gè)具體的屬性做修改,而是每當(dāng) data 變化時(shí)重新去計(jì)算一次。因此,這里為 nodes 添加的響應(yīng)式是無用的。那么怎么把不需要的 proxySetter 去掉呢?一種方法是將 nodes 改回計(jì)算屬性,一般情況下計(jì)算屬性沒有賦值行為。另一種方法就是凍結(jié)數(shù)據(jù)。

使用 Object.freeze() 來凍結(jié)數(shù)據(jù),這會阻止修改現(xiàn)有的屬性,也意味著響應(yīng)系統(tǒng)無法再追蹤變化。

this.nodes = Object.freeze(this.getNodes(this.data));

查看 Performance 工具, getNodes 方法后已經(jīng)沒有 proxySetter 了。

性能指標(biāo)如下,對于初次渲染的提升還是很可觀的。

first rendering: 312.22998046875ms
expanded change: 179.59326171875ms

你可以通過改進(jìn)后的示例 (Demo4) 來觀察組件的性能損耗。

那我們能否用同樣的辦法優(yōu)化 status 的跟蹤呢?答案是否定的,因?yàn)槲覀冃枰ジ?status 中的屬性值 ( changeExpanded )。因此,這種優(yōu)化只適用于其屬性不會被更新,只會更新整個(gè)對象的數(shù)據(jù)。且對于結(jié)構(gòu)越復(fù)雜、層級越深的數(shù)據(jù),優(yōu)化效果越明顯。

替代方案

我們看到,示例中不管是節(jié)點(diǎn)的渲染還是數(shù)據(jù)的計(jì)算,都存在大量的循環(huán)或遞歸。對于這種大量數(shù)據(jù)的問題,除了上述提到的針對 Vue 的優(yōu)化外,我們還可以從減少每次循環(huán)的耗時(shí)和減少循環(huán)次數(shù)兩個(gè)方面進(jìn)行優(yōu)化。

例如,可以使用字典來優(yōu)化數(shù)據(jù)查找。

// 生成 defaultExpandedKeys 的 Map 對象
const expandedKeysMap = this.defaultExpandedKeys.reduce((map, key) => {
 map[key] = true;
 return map;
}, {});

// 查找時(shí)
if (expandedKeysMap[key]) {
 // do something
}

defaultExpandedKeys.includes 的事件復(fù)雜度是 O(n), expandedKeysMap[key] 的時(shí)間復(fù)雜度是 O(1)。

更多關(guān)于優(yōu)化 Vue 應(yīng)用性能可以查看Vue 應(yīng)用性能優(yōu)化指南。

這樣做的價(jià)值

應(yīng)用性能對于用戶體驗(yàn)的提升是非常重要的,也往往是容易被忽視的。試想一下,一個(gè)在某臺設(shè)備運(yùn)行良好的應(yīng)用,到了另一臺配置較差的設(shè)備上導(dǎo)致用戶瀏覽器崩潰了,這一定是一個(gè)不好的體驗(yàn)。又或者你的應(yīng)用在常規(guī)數(shù)據(jù)下正常運(yùn)行,卻在大數(shù)據(jù)量下需要相當(dāng)長的等待時(shí)間,也許你就因此錯(cuò)失了一部分用戶。

總結(jié)

性能優(yōu)化是一個(gè)長久不衰的話題,沒有一種通用的辦法能夠解決所有的性能問題。性能優(yōu)化是可以持續(xù)不端地進(jìn)行下去的,但隨著問題的深入,性能瓶頸會越來越不明顯,優(yōu)化也越困難。

本文的示例具有一定的特殊性,但它為我們指引了性能優(yōu)化的方法論。

  • 確定衡量運(yùn)行時(shí)性能的指標(biāo)
  • 確定優(yōu)化目標(biāo),例如實(shí)現(xiàn) 1W+ 數(shù)據(jù)的秒出
  • 使用工具(Chrome Performance)分析性能問題
  • 優(yōu)先解決問題的大頭(瓶頸)
  • 重復(fù) 3 4 步直到實(shí)現(xiàn)目標(biāo)

以上所述是小編給大家介紹的Cookbook組件形式:優(yōu)化 Vue 組件的運(yùn)行時(shí)性能,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時(shí)回復(fù)大家的!

相關(guān)文章

  • Vue.js每天必學(xué)之過渡與動畫

    Vue.js每天必學(xué)之過渡與動畫

    Vue.js每天必學(xué)之過渡與動畫,對Vue.js過渡與動畫進(jìn)行深入學(xué)習(xí),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • vue如何動態(tài)配置ip與端口

    vue如何動態(tài)配置ip與端口

    這篇文章主要介紹了vue如何動態(tài)配置ip與端口,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • vue3中markRaw示例詳解

    vue3中markRaw示例詳解

    markRaw是將一個(gè)對象標(biāo)記為普通對象,而toRaw是將reactive對象變?yōu)槠胀▽ο?在 Vue 3 中,markRaw 是一個(gè)用于告訴 Vue 的響應(yīng)性系統(tǒng)不要對某個(gè)對象進(jìn)行轉(zhuǎn)換或追蹤其響應(yīng)性的函數(shù),下面給大家介紹vue3中markRaw詳解,感興趣的朋友一起看看吧
    2024-04-04
  • 利用VUE框架,實(shí)現(xiàn)列表分頁功能示例代碼

    利用VUE框架,實(shí)現(xiàn)列表分頁功能示例代碼

    本篇文章主要介紹了利用VUE框架,實(shí)現(xiàn)列表分頁功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-01-01
  • 淺談sass在vue注意的地方

    淺談sass在vue注意的地方

    下面小編就為大家?guī)硪黄獪\談sass在vue注意的地方。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • vue分頁組件table-pagebar使用實(shí)例解析

    vue分頁組件table-pagebar使用實(shí)例解析

    這篇文章主要為大家詳細(xì)解析了vue分頁組件table-pagebar使用實(shí)例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-08-08
  • 詳解vue項(xiàng)目中調(diào)用百度地圖API使用方法

    詳解vue項(xiàng)目中調(diào)用百度地圖API使用方法

    這篇文章主要介紹了vue項(xiàng)目中調(diào)用百度地圖API使用方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 手寫Vue源碼之?dāng)?shù)據(jù)劫持示例詳解

    手寫Vue源碼之?dāng)?shù)據(jù)劫持示例詳解

    這篇文章主要給大家介紹了手寫Vue源碼之?dāng)?shù)據(jù)劫持的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • vue 使用插槽分發(fā)內(nèi)容操作示例【單個(gè)插槽、具名插槽、作用域插槽】

    vue 使用插槽分發(fā)內(nèi)容操作示例【單個(gè)插槽、具名插槽、作用域插槽】

    這篇文章主要介紹了vue 使用插槽分發(fā)內(nèi)容操作,結(jié)合實(shí)例形式總結(jié)分析了vue.js使用單個(gè)插槽、具名插槽、作用域插槽相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下
    2020-03-03
  • vue前端el-input輸入限制輸入位數(shù)及輸入規(guī)則

    vue前端el-input輸入限制輸入位數(shù)及輸入規(guī)則

    這篇文章主要給大家介紹了關(guān)于vue前端el-input輸入限制輸入位數(shù)及輸入規(guī)則的相關(guān)資料,文中通過代碼介紹的介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用vue具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09

最新評論