演示代碼使用 Vue3 + ts + Vite 編寫,但是也會列出適用於 Vue2 的優化技巧,如果某個優化只適用於 Vue3 或者 Vue2,我會在標題中標出來。 代碼優化 v-for 中使用 key 使用 v-for 更新已經渲染的元素列表時,預設用就地復用策略;列表數據修改的時候,他會根據 k ...
演示代碼使用 Vue3 + ts + Vite 編寫,但是也會列出適用於 Vue2 的優化技巧,如果某個優化只適用於 Vue3 或者 Vue2,我會在標題中標出來。
代碼優化
v-for 中使用 key
使用 v-for
更新已經渲染的元素列表時,預設用就地復用策略;列表數據修改的時候,他會根據 key
值去判斷某個值是否修改,如果修改,則重新渲染這一項,否則復用之前的元素;
使用key的註意事項:
- 不要使用可能重覆的或者可能變化
key
值(控制台也會給出提醒) - 如果數組中的數據有狀態需要維持時(例如輸入框),不要使用數組的
index
作為key
值,因為如果在數組中插入或者移除一個元素時,其後面的元素 index 將會變化,這回讓vue進行原地復用時錯誤的綁定狀態。 - 如果數組中沒有唯一的 key值可用,且數組更新時不是全量更新而是採用類似push,splice來插入或者移除數據時,可以考慮對其添加一個 key 欄位,值為 Symbol() 即可保證唯一。
何時使用何種key?
這是一個非常有考究的問題,首先你要知道 vue 中的 原地復用
(大概就是 虛擬dom
變化時,兩個 虛擬dom節點
的 key
如果一樣就不會重新創建節點,而是修改原來的節點)。
當我們渲染的數據不需要保持狀態時,例如常見的單純的表格分頁渲染(不包含輸入,只是展示)、下拉載入更多等場景,那麼使用 index
作為 key
再好不過,因為進入下一頁或者上一頁時就會原地復用之前的節點,而不是重新創建,如果使用唯一的 id
作為 key
反而會重新創建dom,性能相對較低。
此外使用 index
作為 key
我還應該要儘量避免對數組的中間進行 增加/刪除 等會影響後面元素key變化的操作。這會讓 vue 認為後面所有元素都發生了變化,導致多餘的對比和原地復用。
所以使用 index 作為 key 需要滿足:
- 數據沒有獨立的狀態
- 數據不會進行 增加/刪除 等會影響後面元素key變化的操作
哪何時使用 id 作為 key 呢?
對於大多數數據的 id
都是唯一的,這無疑的一個 key
的優選答案。對於任何大多數情況使用 id
作為 key
都不會出現上面 bug
。但是如果你需要考慮性能問題,那就就要思考是否應該使用原地復用了。
同樣是上面的分頁數據展示,如果使用 id
作為 key
,可想而知每一頁的每一條數據 id
都是不一樣的,所以當換頁時兩顆 虛擬DOM樹
的節點的 key
完全不一致,vue
就會移除原來的節點然後創建新的節點。可想而知效率會更加低下。但是他也有它的優點。唯一的 key
可以幫助 diff
更加精確的為我們綁定狀態,這尤其適合數據有獨立的狀態的場景,例如帶輸入框或者單選框的列表數據。
所以何時使用 id 作為 key?只有一點:
- 無法使用 index 作為 key 的時候
v-for 和 v-if 不要一起使用(Vue2)
此優化技巧僅限於Vue2,Vue3 中對 v-for 和 v-if 的優先順序做了調整
這個大家都知道
永遠不要把
v-if
和v-for
同時用在同一個元素上。
原因是 v-for
的 優先順序高於 v-if
,所以當它們使用再同一個標簽上是,每一個渲染都會先迴圈再進行條件判斷。
註意: Vue3 中
v-if
優先順序高於v-for
,所以當v-for
和v-if
一起使用時效果類似於Vue2
中把v-if
上提的效果
例如下麵這段代碼在 Vue2
中是不被推薦的,Vue
也會給出對應的警告.
<ul>
<li v-for="user in users" v-if="user.active">
{{ user.name }}
</li>
</ul>
我們應該儘量將 v-if
移動到上級或者使用計算屬性來處理數據.
<ul v-if="active">
<li v-for="user in users">
{{ user.name }}
</li>
</ul>
如果你不想讓迴圈的內容多出一個無需有的上級容器,那麼你可以選擇使用 template
來作為其父元素,template
不會被瀏覽器渲染為 DOM
節點.
如果我想要判斷遍歷對象裡面每一項的內容來選擇渲染的數據的話,可以使用 computed
來對遍歷對象進行過濾.
// js
let usersActive = computed(()=>users.filter(user => user.active))
// template
<ul>
<li v-for="user in usersActive">
{{ user.name }}
</li>
</ul>
合理的選擇 v-if 和 v-show
v-if
和 v-show
的區別相比大家都非常熟悉了;v-if
通過直接操作 DOM
的刪除和添加來控制元素的顯示和隱藏;v-show
是通過控制 DOM
的 display
CSS熟悉來控制元素的顯示和隱藏.
由於對 DOM 的 添加/刪除 操作性能遠遠低於操作 DOM 的 CSS 屬性.
所以當元素需要頻繁的 顯示/隱藏 變化時,我們使用 v-show
來提高性能。
當元素不需要頻繁的 顯示/隱藏 變化時,我們通過 v-if
來移除 DOM 可以節約掉瀏覽器渲染這個的一部分DOM需要的資源.
使用簡單的計算屬性
應該把複雜計算屬性分割為儘可能多的更簡單的 property。
- 易於測試
當每個計算屬性都包含一個非常簡單且很少依賴的表達式時,撰寫測試以確保其正確工作就會更加容易。- 易於閱讀
簡化計算屬性要求你為每一個值都起一個描述性的名稱,即便它不可復用。這使得其他開發者 (以及未來的你) 更容易專註在他們關心的代碼上並搞清楚發生了什麼。- 更好的“擁抱變化”
任何能夠命名的值都可能用在視圖上。舉個例子,我們可能打算展示一個信息,告訴用戶他們存了多少錢;也可能打算計算稅費,但是可能會分開展現,而不是作為總價的一部分。
小的、專註的計算屬性減少了信息使用時的假設性限制,所以需求變更時也用不著那麼多重構了。
computed 大家很熟悉, 它會在其表達式中依賴的響應式數據發送變化時重新計算。如果我們在一個計算屬性中書寫了比較複雜的表達式,那麼其依賴的響應式數據也任意變得更多。當其中任何一個依賴項變化時整個表達式都需要重新計算.
let price = computed(()=>{
let basePrice = manufactureCost / (1 - profitMargin)
return (
basePrice -
basePrice * (discountPercent || 0)
)
})
當 manufactureCost、profitMargin、discountPercent 中任何一個變化時都會重新計算整個 price。
但是如果我們改成下麵這樣
let basePrice = computed(() => manufactureCost / (1 - profitMargin))
let discount = computed(() => basePrice * (discountPercent || 0))
let finalPrice = computed(() => basePrice - discount)
如果當 discountPercent 變化時,只會 重新計算 discount 和 finalPrice,由於 computed 的緩存特性,不會重新計算 basePrice.
functional 函數式組件(Vue2)
註意:這僅僅在 Vue2 中被作為一種優化手段,在 3.x 中,有狀態組件和函數式組件之間的性能差異已經大大減少,並且在大多數用例中是微不足道的。因此,在 SFC 上使用 functional
的開發人員的遷移路徑是刪除該 attribute,並將 props
的所有引用重命名為 $props
,將 attrs
重命名為 $attrs
。
優化前:
<template>
<div class="cell">
<div v-if="value" class="on"></div>
<section v-else class="off"></section>
</div>
</template>
<script>
export default {
props: ['value'],
}
</script>
優化後:
<template functional>
<div class="cell">
<div v-if="props.value" class="on"></div>
<section v-else class="off"></section>
</div>
</template>
<script>
export default {
props: ['value'],
}
</script>
- 沒有this(沒有實例)
- 沒有響應式數據
拆分組件
什麼?你寫的一個vue文件有一千多行代碼?