自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一頓操作,我把 Table 組件性能提升了十倍

開發(fā) 前端
Table 表格組件在 Web 開發(fā)中的應(yīng)用隨處可見,不過當(dāng)表格數(shù)據(jù)量大后,伴隨而來的是性能問題:渲染的 DOM 太多,渲染和交互都會有一定程度的卡頓。

  [[423502]]

背景

Table 表格組件在 Web 開發(fā)中的應(yīng)用隨處可見,不過當(dāng)表格數(shù)據(jù)量大后,伴隨而來的是性能問題:渲染的 DOM 太多,渲染和交互都會有一定程度的卡頓。

通常,我們有兩種優(yōu)化表格的方式:一種是分頁,另一種是虛擬滾動。這兩種方式的優(yōu)化思路都是減少 DOM 渲染的數(shù)量。在我們公司的項目中,會選擇分頁的方式,因為虛擬滾動不能正確的讀出行的數(shù)量,會有 Accessibility 的問題。

記得 19 年的時候,我在 Zoom 已經(jīng)推行了基于 Vue.js 的前后端分離的優(yōu)化方案,并且基于 ElementUI 組件庫開發(fā)了 ZoomUI。其中我們在重構(gòu)用戶管理頁面的時候使用了 ZoomUI 的 Table 組件替換了之前老的用 jQuery 開發(fā)的 Table 組件。

因為絕大部分場景 Table 組件都是分頁的,所以并不會有性能問題。但是在某個特殊場景下:基于關(guān)鍵詞的搜索,可能會出現(xiàn) 200 * 20 條結(jié)果且不分頁的情況,且表格是有一列是帶有 checkbox 的,也就是可以選中某些行進行操作。

當(dāng)我們?nèi)c選其中一行時,發(fā)現(xiàn)過了好久才選中,有明顯的卡頓感,而之前的 jQuery 版本卻沒有這類問題,這一比較令人大跌眼鏡。難道好好的技術(shù)重構(gòu),卻要犧牲用戶體驗嗎?

Table 組件第一次優(yōu)化嘗試

既然有性能問題,那么我們的第一時間的思路應(yīng)該是要找出產(chǎn)生性能問題的原因。

列展示優(yōu)化

首先,ZoomUI 渲染的 DOM 數(shù)量是要多于 jQuery 渲染的 Table 的,因此第一個思考方向是讓 Table 組件 盡可能地減少 DOM 的渲染數(shù)量 。

20 列數(shù)據(jù)通常在屏幕下是展示不全的,老的 jQuery Table 實現(xiàn)很簡單,底部有滾動條,而 ZoomUI 在這種列可滾動的場景下,支持了左右列的固定,這樣在左右滑動過程中,可以固定某些列一直展示,用戶體驗更好,但這樣的實現(xiàn)是有一定代價的。

想要實現(xiàn)這種固定列的布局,ElementUI 用了 6 個 table 標(biāo)簽來實現(xiàn),那么為什么需要 6 個 table 標(biāo)簽?zāi)兀?/p>

首先,為了讓 Table 組件支持豐富的表頭功能,表頭和表體都是各自用一個 table 標(biāo)簽來實現(xiàn)。因此對于一個表格來說,就會有 2 個 table 標(biāo)簽,那么再加上左側(cè) fixed 的表格,和右側(cè) fixed 的表格,總共有 6 個 table 標(biāo)簽。

在 ElementUI 實現(xiàn)中,左側(cè) fixed 表格和右側(cè) fixed 表格從 DOM 上都渲染了完整的列,然后從樣式上控制它們的顯隱:

但這么實現(xiàn)是有性能浪費的,因為完全不需要渲染這么多列,實際上只需要渲染固定展示的列的 DOM,然后做好高度同步即可。 ZoomUI 就是這么實現(xiàn)的,效果如下:

當(dāng)然,僅僅減少 fixed 表格渲染的列,性能的提升還不夠明顯,有沒有辦法在列的渲染這個維度繼續(xù)優(yōu)化呢?

這就是從業(yè)務(wù)層面的優(yōu)化了,對于一個 20 列的表格,往往關(guān)鍵的列并沒有多少,那么我們可不可以初次渲染僅僅渲染關(guān)鍵的列,其它列通過配置方式的渲染呢?

根據(jù)上述需求,我給 Table 組件添加了如下功能:

Table 組件新增一個 initDisplayedColumn 屬性,通過它可以配置初次渲染的列,同時當(dāng)用戶修改了初次渲染的列,會在前端存儲下來,便于下一次的渲染。

通過這種方式,我們就可以少渲染一些列。顯然,列渲染少了,表格整體渲染的 DOM 數(shù)就會變少,對性能也會有一定的提升。

更新渲染的優(yōu)化

當(dāng)然,僅僅通過優(yōu)化列的渲染還是不夠的,我們遇到的問題是當(dāng)點選某一行引起的渲染卡頓,為什么會引起卡頓呢?

為了定位該問題,我用 Table 組件創(chuàng)建了一個 1000 * 7 的表格,開啟了 Chrome 的 Performance 面板記錄 checkbox 點選前后的性能。

在經(jīng)過幾次 checkbox 選擇框的點選后,可以看到如下火焰圖:

其中黃色部分是 Scripting 腳本的執(zhí)行時間,紫色部分是 Rendering 所占的時間。我們再截取一次更新的過程:

然后觀察 JS 腳本執(zhí)行的 Call Tree,發(fā)現(xiàn)時間主要花在了 Table 組件的更新渲染上 

我們發(fā)現(xiàn)組件的 render to vnode 花費的時間約 600ms; vnode patch to DOM 花費的時間約 160ms。

為什么會需要這么長時間呢,因為點選了 checkbox ,在組件內(nèi)部修改了其維護的選中狀態(tài)數(shù)據(jù),而整個組件的 render 過程中又訪問了這個狀態(tài)數(shù)據(jù),因此當(dāng)這個數(shù)據(jù)修改后,會引發(fā)整個組件的重新渲染。

而又由于有 1000 * 7 條數(shù)據(jù),因此整個表格需要循環(huán) 1000 * 7 次去創(chuàng)建最內(nèi)部的 td ,整個過程就會耗時較長。

那么循環(huán)的內(nèi)部是不是有優(yōu)化的空間呢?對于 ElementUI 的 Table 組件,這里有非常大的優(yōu)化空間。

其實優(yōu)化思路主要參考我之前寫的 《揭秘 Vue.js 九個性能優(yōu)化技巧》 其中的 Local variables 技巧。舉個例子,在 ElementUI 的 Table 組件中,在渲染每個 td 的時候,有這么一段代碼:

  1. const data = { 
  2.   store: this.store, 
  3.   _self: this.context || this.table.$vnode.context, 
  4.   column: columnData, 
  5.   row, 
  6.   $index 

這樣的代碼相信很多小伙伴隨手就寫了,但卻忽視了其內(nèi)部潛在的性能問題。

由于 Vue.js 響應(yīng)式系統(tǒng)的設(shè)計,在每次訪問 this.store 的時候,都會觸發(fā) 響應(yīng)式數(shù)據(jù)內(nèi)部的  getter  函數(shù),進而執(zhí)行 它的依賴收集,當(dāng)這段代碼被循環(huán)了 1000 * 7 次,就會執(zhí)行 this.store 7000 次的依賴收集,這就造成了性能的浪費,而真正的依賴收集只需要執(zhí)行一次就足夠了。

解決這個問題其實也并不難,由于 Table 組件中的 TableBody 組件是用 render 函數(shù)寫的,我們可以在組件 render 函數(shù)的入口處定義一些局部變量:

  1. render(h) { 
  2.   const { store /*...*/} = this 
  3.   const context = this.context ||  this.table.$vnode.context 

然后在渲染整個 render 的過程中,把局部變量當(dāng)作內(nèi)部函數(shù)的參數(shù)傳入,這樣在內(nèi)部渲染 td 的渲染中再次訪問這些變量就不會觸發(fā)依賴收集了:

  1. rowRender({store, context, /* ...其它變量 */}) { 
  2.   const data = { 
  3.     store: store, 
  4.     _self: context, 
  5.     column: columnData, 
  6.     row, 
  7.     $index, 
  8.     disableTransition, 
  9.     isSelectedRow 
  10.   } 

通過這種方式,我們把類似的代碼都做了修改,就實現(xiàn)了 TableBody 組件渲染函數(shù)內(nèi)部訪問這些響應(yīng)式變量,只觸發(fā)一次依賴收集的效果,從而優(yōu)化了 render 的性能。

來看一下優(yōu)化后的火焰圖:

從面積上看似乎 Scripting 的執(zhí)行時間變少了,我們再來看它一次更新所需要的 JS 執(zhí)行時間:

我們發(fā)現(xiàn)組件的 render to vnode 花費的時間約 240ms; vnode patch to DOM 花費的時間約 127ms。

可以看到,ZoomUI Table 組件的 render 的時間和 update 的時間都要明顯少于 ElementUI 的 Table 組件。 render 時間減少是由于響應(yīng)式變量依賴收集的時間大大減少, update 的時間的減少是因為 fixed 表格渲染的 DOM 數(shù)量減少。

從用戶的角度來看,DOM 的更新除了 Scripting 的時間,還有 Rendering 的時間,它們是共享一個線程的,當(dāng)然由于 ZoomUI Table 組件渲染的 DOM 數(shù)量更少,執(zhí)行 Rendering 的時間也更短。

手寫 benchmark

僅僅從 Performance 面板的測試并不是一個特別精確的 benchmark,我們可以針對 Table 組件手寫一個 benchmark。

我們可以先創(chuàng)建一個按鈕,去模擬 Table 組件的選中操作:

  1. <div> 
  2.   <zm-button @click="toggleSelection(computedData[1]) 
  3. ">切換第二行選中狀態(tài) 
  4.   </zm-button> 
  5. </div> 
  6. <div> 
  7.   更新所需時間: {{ renderTime }} 
  8. </div> 

然后實現(xiàn)這個 toggleSelection 函數(shù):

  1. methods: { 
  2.  toggleSelection(row) { 
  3.    const s = window.performance.now() 
  4.    if (row) { 
  5.      this.$refs.table.toggleRowSelection(row) 
  6.    } 
  7.    setTimeout(() => { 
  8.      this.renderTime = (window.performance.now() - s).toFixed(2) + 'ms' 
  9.    }) 
  10.  } 

我們在點擊事件的回調(diào)函數(shù)中,通過 window.performance.now() 記錄起始時間,然后在 setTimeout 的回調(diào)函數(shù)中,再去通過時間差去計算整個更新渲染需要的時間。

由于 JS 的執(zhí)行和 UI 渲染占用同一線程,因此在一個宏任務(wù)執(zhí)行過程中,會執(zhí)行這倆任務(wù),而 setTimeout 0 會把對應(yīng)的回調(diào)函數(shù)添加到下一個宏任務(wù)中,當(dāng)該回調(diào)函數(shù)執(zhí)行,說明上一個宏任務(wù)執(zhí)行完畢,此時做時間差去計算性能是相對精確的。

基于手寫的 benchmark 得到如下測試結(jié)果:

ElementUI Table 組件一次更新的時間約為 900ms 。

ZoomUI Table 組件一次更新的時間約為 280ms,相比于 ElementUI 的 Table 組件, 性能提升了約三倍 。

v-memo 的啟發(fā)

經(jīng)過這一番優(yōu)化,基本解決了文章開頭提到的問題,在 200 * 20 的表格中去選中一列,已經(jīng)并無明顯的卡頓感了,但相比于 jQuery 實現(xiàn)的 Table,效果還是要差了一點。

雖然性能優(yōu)化了三倍,但我還是有個心結(jié):明明只更新了一行數(shù)據(jù)的選中狀態(tài),卻還是重新渲染了整個表格,仍然需要在組件 render 的過程中執(zhí)行多次的循環(huán),在 patch 的過程中通過 diff 算法來對比更新。

最近我研究了 Vue.js 3.2 v-memo 的實現(xiàn),看完源碼后,我非常激動,因為發(fā)現(xiàn)這個優(yōu)化技巧似乎可以應(yīng)用到 ZoomUI 的 Table 組件中,盡管我們的組件庫是基于 Vue 2 版本開發(fā)的。

我花了一個下午的時間,經(jīng)過一番嘗試,果然成功了,那么具體是怎么做的呢?先不著急,我們從 v-memo 的實現(xiàn)原理說起。

v-memo 的實現(xiàn)原理

v-memo 是 Vue.js 3.2 版本新增的指令,它可以用于普通標(biāo)簽,也可以用于列表,結(jié)合 v-for 使用,在官網(wǎng)文檔中,有這么一段介紹:

v-memo 僅供性能敏感場景的針對性優(yōu)化,會用到的場景應(yīng)該很少。渲染 v-for 長列表 (長度大于 1000) 可能是它最有用的場景:

  1. <div v-for="item in list" :key="item.id" v-memo="[item.id === selected]"
  2.   <p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p> 
  3.   <p>...more child nodes</p> 
  4. </div> 

當(dāng)組件的 selected 狀態(tài)發(fā)生變化時,即使絕大多數(shù) item 都沒有發(fā)生任何變化,大量的 VNode 仍將被創(chuàng)建。此處使用的 v-memo 本質(zhì)上代表著“僅在 item 從未選中變?yōu)檫x中時更新它,反之亦然”。這允許每個未受影響的 item 重用之前的 VNode,并完全跳過差異比較。注意,我們不需要把 item.id 包含在記憶依賴數(shù)組里面,因為 Vue 可以自動從 item 的 :key 中把它推斷出來。

其實說白了 v-memo 的核心就是復(fù)用 vnode ,上述模板借助于在線模板編譯工具,可以看到其對應(yīng)的 render 函數(shù):

  1. import { renderList as _renderList, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock, toDisplayString as _toDisplayString, createElementVNode as _createElementVNode, isMemoSame as _isMemoSame, withMemo as _withMemo } from "vue" 
  2.  
  3. const _hoisted_1 = /*#__PURE__*/_createElementVNode("p"null"...more child nodes", -1 /* HOISTED */
  4.  
  5. export function render(_ctx, _cache, $props, $setup, $data, $options) { 
  6.   return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, (item, __, ___, _cached) => { 
  7.     const _memo = ([item.id === _ctx.selected]) 
  8.     if (_cached && _cached.key === item.id && _isMemoSame(_cached, _memo)) return _cached 
  9.     const _item = (_openBlock(), _createElementBlock("div", { 
  10.       key: item.id 
  11.     }, [ 
  12.       _createElementVNode("p"null"ID: " + _toDisplayString(item.id) + " - selected: " + _toDisplayString(item.id === _ctx.selected), 1 /* TEXT */), 
  13.       _hoisted_1 
  14.     ])) 
  15.     _item.memo = _memo 
  16.     return _item 
  17.   }, _cache, 0), 128 /* KEYED_FRAGMENT */)) 

基于 v-for 的列表內(nèi)部是通過 renderList 函數(shù)來渲染的,來看它的實現(xiàn):

  1. function renderList(source, renderItem, cache, index) { 
  2.   let ret 
  3.   const cached = (cache && cache[index]) 
  4.   if (isArray(source) || isString(source)) { 
  5.     ret = new Array(source.length) 
  6.     for (let i = 0, l = source.length; i < l; i++) { 
  7.       ret[i] = renderItem(source[i], i, undefined, cached && cached[i]) 
  8.     } 
  9.   } 
  10.   else if (typeof source === 'number') { 
  11.     // source 是數(shù)字 
  12.   } 
  13.   else if (isObject(source)) { 
  14.     // source 是對象 
  15.   } 
  16.   else { 
  17.     ret = [] 
  18.   } 
  19.   if (cache) { 
  20.     cache[index] = ret 
  21.   } 
  22.   return ret 

我們只分析 source ,也就是列表 list 是數(shù)組的情況,對于每一個 item ,會執(zhí)行 renderItem 函數(shù)來渲染。

從生成的 render 函數(shù)中,可以看到 renderItem 的實現(xiàn)如下:

  1. (item, __, ___, _cached) => { 
  2.     const _memo = ([item.id === _ctx.selected]) 
  3.     if (_cached && _cached.key === item.id && _isMemoSame(_cached, _memo)) return _cached 
  4.     const _item = (_openBlock(), _createElementBlock("div", { 
  5.       key: item.id 
  6.     }, [ 
  7.       _createElementVNode("p"null"ID: " + _toDisplayString(item.id) + " - selected: " + _toDisplayString(item.id === _ctx.selected), 1 /* TEXT */), 
  8.       _hoisted_1 
  9.     ])) 
  10.     _item.memo = _memo 
  11.     return _item 
  12.   } 

在 renderItem 函數(shù)內(nèi)部,維護了一個 _memo 變量,它就是用來判斷是否從緩存里獲取 vnode 的條件數(shù)組;而第四個參數(shù) _cached 對應(yīng)的就是 item 對應(yīng)緩存的 vnode 。接下來通過 isMemoSame 函數(shù)來判斷 memo 是否相同,來看它的實現(xiàn):

  1. function isMemoSame(cached, memo) { 
  2.   const prev = cached.memo 
  3.   if (prev.length != memo.length) { 
  4.     return false 
  5.   } 
  6.   for (let i = 0; i < prev.length; i++) { 
  7.     if (prev[i] !== memo[i]) { 
  8.       return false 
  9.     } 
  10.   } 
  11.   // ... 
  12.   return true 

isMemoSame 函數(shù)內(nèi)部會通過 cached.memo 拿到緩存的 memo ,然后通過遍歷對比每一個條件來判斷和當(dāng)前的 memo 是否相同。

而在 renderItem 函數(shù)的結(jié)尾,就會把 _memo 緩存到當(dāng)前 item 的 vnode 中,便于下一次通過   isMemoSame 來判斷這個 memo 是否相同,如果相同,說明該項沒有變化,直接返回上一次緩存的 vnode 。

那么這個緩存的 vnode 具體存儲到哪里呢,原來在初始化組件實例的時候,就設(shè)計了渲染緩存:

  1. const instance = { 
  2.   // ... 
  3.   renderCache: [] 

然后在執(zhí)行 render 函數(shù)的時候,把這個緩存當(dāng)做第二個參數(shù)傳入:

  1. const { renderCache } = instance 
  2. result = normalizeVNode( 
  3.   render.call( 
  4.     proxyToUse, 
  5.     proxyToUse, 
  6.     renderCache, 
  7.     props, 
  8.     setupState, 
  9.     data, 
  10.     ctx 
  11.   ) 

然后在執(zhí)行 renderList 函數(shù)的時候,把 _cahce 作為第三個參數(shù)傳入:

  1. export function render(_ctx, _cache, $props, $setup, $data, $options) { 
  2.   return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, (item, __, ___, _cached) => { 
  3.     // renderItem 實現(xiàn) 
  4.   }, _cache, 0), 128 /* KEYED_FRAGMENT */)) 

所以實際上列表緩存的 vnode 都保留在 _cache 中,也就是 instance.renderCache 中。

那么為啥使用緩存的 vnode 就能優(yōu)化 patch 過程呢,因為在 patch 函數(shù)執(zhí)行的時候,如果遇到新舊 vnode 相同,就直接返回,什么也不用做了。

  1. const patch = (n1, n2, container, anchor = null, parentComponent = null, parentSuspense = null, isSVG = false, slotScopeIds = null, optimized = false) => { 
  2.   if(n1 === n2) { 
  3.     return 
  4.   } 
  5.   // ... 

顯然,由于使用緩存的 vnode ,它們 指向同一個對象引用 ,直接返回,節(jié)約了后續(xù)執(zhí)行 patch 過程的時間。

在 Table 組件的應(yīng)用

v-memo 的優(yōu)化思路很簡單,就是復(fù)用緩存的 vnode ,這是一種空間換時間的優(yōu)化思路。

那么,前面我們提到在表格組件中選擇狀態(tài)沒有變化的行,是不是也可以從緩存中獲取呢?

順著這思路,我給 Table 組件設(shè)計了 useMemo 這個 prop ,它其實是專門用于有選擇列的場景。

然后在 TableBody 組件的 created 鉤子函數(shù)中,創(chuàng)建了用于緩存的對象:

  1. created() { 
  2.   if (this.table.useMemo) { 
  3.     if (!this.table.rowKey) { 
  4.       throw new Error('for useMemo, row-key is required.'
  5.     } 
  6.     this.vnodeCache = [] 
  7.   } 

這里之所以把 vnodeCache 定義到 created 鉤子函數(shù)中,是因為它并不需要變成響應(yīng)式對象。

另外注意,我們會根據(jù)每一行的 key 作為緩存的 key ,因此 Table 組件的 rowKey 屬性是必須的。

然后在渲染每一行的過程中,添加了 useMemo 相關(guān)的邏輯:

  1. function rowRender({ /* 各種變量參數(shù) */}) { 
  2.   let memo 
  3.   const key = this.getKeyOfRow({ row, rowIndex: $index, rowKey }) 
  4.   let cached 
  5.   if (useMemo) { 
  6.     cached = this.vnodeCache[key] 
  7.     const currentSelection = store.states.selection 
  8.     if (cached && !this.isRowSelectionChanged(row, cached.memo, currentSelection)) { 
  9.       return cached 
  10.     } 
  11.     memo = currentSelection.slice() 
  12.   } 
  13.   // 渲染 row,返回對應(yīng)的 vnode 
  14.   const ret = rowVnode 
  15.   if (useMemo && columns.length) { 
  16.     ret.memo = memo 
  17.     this.vnodeCache[key] = ret 
  18.    } 
  19.    return ret 

這里的 memo 變量用于記錄已選中的行數(shù)據(jù),并且它也會在函數(shù)最后存儲到 vnode 的 memo ,便于下一次的比對。

在每次渲染 row 的 vnode 前,會根據(jù) row 對應(yīng)的 key 嘗試從緩存中?。蝗绻彺嬷写嬖?,再通過 isRowSelectionChanged 來判斷行的選中狀態(tài)是否改變;如果沒有改變,則直接返回緩存的 vnode 。

如果沒有命中緩存或者是行選擇狀態(tài)改變,則會去重新渲染拿到新的 rowVnode ,然后更新到 vnodeCache 中。

當(dāng)然,這種實現(xiàn)相比于 v-memo 沒有那么通用,只去對比行選中的狀態(tài)而不去對比其它數(shù)據(jù)的變化。你可能會問,如果這一行某列的數(shù)據(jù)修改了,但選中狀態(tài)沒變,再走緩存不就不對了嗎?

確實存在這個問題,但是在我們的使用場景中,遇到數(shù)據(jù)修改,是會發(fā)送一個異步請求到后端,然獲取新的數(shù)據(jù)再來更新表格數(shù)據(jù)。因此我只需要觀測表格數(shù)據(jù)的變化清空 vnodeCache 即可:

  1. watch: { 
  2.   'store.states.data'() { 
  3.     if (this.table.useMemo) { 
  4.       this.vnodeCache = [] 
  5.     } 
  6.   } 

此外,我們支持列的可選則渲染功能,以及在窗口發(fā)生變化時,隱藏列也可能發(fā)生變化,于是在這兩種場景下,也需要清空 vnodeCache :

  1. watch:{ 
  2.   'store.states.columns'() { 
  3.     if (this.table.useMemo) { 
  4.       this.vnodeCache = [] 
  5.     } 
  6.   }, 
  7.   columnsHidden(newVal, oldVal) { 
  8.     if (this.table.useMemo && !valueEquals(newVal, oldVal)) { 
  9.       this.vnodeCache = [] 
  10.     } 
  11.   } 

以上實現(xiàn)就是基于 v-memo 的思路實現(xiàn)表格組件的性能優(yōu)化。我們從火焰圖上看一下它的效果:

我們發(fā)現(xiàn)黃色的 Scripting 時間幾乎沒有了,再來看它一次更新所需要的 JS 執(zhí)行時間:

我們發(fā)現(xiàn)組件的 render to vnode 花費的時間約 20ms ; vnode patch to DOM  花費的時間約 1ms ,整個更新渲染過程, JS 的執(zhí)行時間大幅減少。

另外,我們通過 benchmark 測試,得到如下結(jié)果:

優(yōu)化后,ZoomUI Table 組件一次更新的時間約為 80ms ,相比于 ElementUI 的 Table 組件, 性能提升了約十倍 。

這個優(yōu)化效果還是相當(dāng)驚人的,并且從性能上已經(jīng)不輸 jQuery Table 了,我兩年的心結(jié)也隨之解開了。

總結(jié)

Table 表格性能提升主要是三個方面:減少 DOM 數(shù)量、優(yōu)化 render 過程以及復(fù)用 vnode 。有些時候,我們還可以從業(yè)務(wù)角度思考,去做一些優(yōu)化。

雖然 useMemo 的實現(xiàn)還比較粗糙,但它目前已滿足我們的使用場景了,并且當(dāng)數(shù)據(jù)量越大,渲染的行列數(shù)越多,這種優(yōu)化效果就越明顯。如果未來有更多的需求,更新迭代就好。

由于一些原因,我們公司仍然在使用 Vue 2,但這并不妨礙我去學(xué)習(xí) Vue 3,了解它一些新特性的實現(xiàn)原理以及設(shè)計思想,能讓我開拓不少思路。

從分析定位問題到最終解決問題,希望這篇文章能給你在組件的性能優(yōu)化方面提供一些思路,并應(yīng)用到日常工作中。

 

 

 

責(zé)任編輯:張燕妮 來源: 老黃的前端私房菜
相關(guān)推薦

2022-04-21 07:51:51

場景JavaSQL

2023-09-07 11:29:36

API開發(fā)

2022-09-21 17:43:29

Kafka底層網(wǎng)絡(luò)

2024-07-17 08:25:44

2024-10-29 08:21:05

2017-09-26 14:56:57

MongoDBLBS服務(wù)性能

2020-03-29 08:56:07

文件系統(tǒng)磁盤Java

2021-02-02 15:38:19

Disruptor緩存Java

2025-03-13 11:59:00

2017-12-06 08:06:47

IBMGPU機器學(xué)習(xí)

2023-06-13 13:52:00

Java 7線程池

2022-09-27 18:19:32

Java數(shù)據(jù)結(jié)構(gòu)

2021-04-13 14:25:41

架構(gòu)運維技術(shù)

2021-12-27 06:57:40

Maven工具性能

2022-02-07 15:05:07

模型AI訓(xùn)練

2009-12-15 21:49:05

2023-02-06 07:17:22

2024-06-27 11:22:34

2022-09-09 09:33:14

支付寶代碼性能

2022-10-15 07:49:18

代碼虛擬線程
點贊
收藏

51CTO技術(shù)棧公眾號