Vue常用性能優化
Vue
常用的一些優化方式,主要是在構建項目過程需要注意的方面。
編碼優化
避免響應所有數據
不要將所有的數據都放到data
中,data
中的數據都會增加getter
和setter
,並且會收集watcher
,這樣還占內存,不需要響應式的數據我們可以直接定義在實例上。
<template>
<view>
</view>
</template>
<script>
export default {
components: {},
data: () => ({
}),
beforeCreate: function(){
this.timer = null;
}
}
</script>
<style scoped>
</style>
函數式組件
函數組是一個不包含狀態和實例的組件,簡單的說,就是組件不支持響應式,並且不能通過this
關鍵字引用自己。因為函數式組件沒有狀態,所以它們不需要像Vue
的響應式系統一樣需要經過額外的初始化,這樣就可以避免相關操作帶來的性能消耗。當然函數式組件仍然會對相應的變化做出響應式改變,比如新傳入新的props
,但是在組件本身中,它無法知道數據何時發生了更改,因為它不維護自己的狀態。很多場景非常適合使用函數式組件:
- 一個簡單的展示組件,也就是所謂的
dumb
組件。例如buttons
、pills
、tags
、cards
等,甚至整個頁面都是靜態文本,比如About
頁面。 - 高階組件,即用於接收一個組件作為參數,返回一個被包裝過的組件。
v-for
循環中的每項通常都是很好的候選項。
區分computed和watch使用場景
computed
是計算屬性,依賴其它屬性值,並且computed
的值有緩存,只有它依賴的屬性值發生改變,下一次獲取computed
的值時才會重新計算computed
的值。
watch
更多的是觀察的作用,類似於某些數據的監聽回調,每當監聽的數據變化時都會執行回調進行后續操作。
當我們需要進行數值計算,並且依賴於其它數據時,應該使用computed
,因為可以利用computed
的緩存特性,避免每次獲取值時,都要重新計算。當我們需要在數據變化時執行異步或開銷較大的操作時,應該使用watch
,使用watch
選項允許我們執行異步操作,限制我們執行該操作的頻率,並在我們得到最終結果前,設置中間狀態。
v-for添加key且避免同時使用v-if
v-for
遍歷必須為item
添加key
,且盡量不要使用index
而要使用唯一id
去標識item
,在列表數據進行遍歷渲染時,設置唯一key
值方便Vue.js
內部機制精准找到該條列表數據,當state
更新時,新的狀態值和舊的狀態值對比,較快地定位到diff
。v-for
遍歷避免同時使用v-if
,v-for
比v-if
優先級高,如果每一次都需要遍歷整個數組,將會影響速度。
區分v-if與v-show使用場景
- 實現方式:
v-if
是動態的向DOM
樹內添加或者刪除DOM
元素,v-show
是通過設置DOM
元素的display
樣式屬性控制顯隱。 - 編譯過程:
v-if
切換有一個局部編譯卸載的過程,切換過程中合適地銷毀和重建內部的事件監聽和子組件,v-show
只是簡單的基於CSS
切換。 - 編譯條件:
v-if
是惰性的,如果初始條件為假,則什么也不做,只有在條件第一次變為真時才開始局部編譯,v-show
是在任何條件下都被編譯,然后被緩存,而且DOM
元素保留。 - 性能消耗:
v-if
有更高的切換消耗,v-show
有更高的初始渲染消耗。 - 使用場景:
v-if
適合條件不太可能改變的情況,v-show
適合條件頻繁切換的情況。
長列表性能優化
Vue
會通過Object.defineProperty
對數據進行劫持,來實現視圖響應數據的變化,然而有些時候我們的組件就是純粹的數據展示,不會有任何改變,我們就不需要Vue
來劫持我們的數據,在大量數據展示的情況下,這能夠很明顯的減少組件初始化的時間,可以通過Object.freeze
方法來凍結一個對象,一旦被凍結的對象就再也不能被修改了。對於需要修改的長列表的優化大列表兩個核心,一個分段一個區分,具體執行分為:僅渲染視窗可見的數據、進行函數節流、 減少駐留的VNode
和Vue
組件,不使用顯示的子組件slot
方式,改為手動創建虛擬DOM
來切斷對象引用。
export default {
data: () => ({
users: {}
}),
async created() {
const users = await axios.get("/api/users");
this.users = Object.freeze(users);
}
};
路由懶加載
Vue
是單頁面應用,可能會有很多的路由引入,這樣使用webpcak
打包后的文件很大,當進入首頁時,加載的資源過多,頁面會出現白屏的情況,不利於用戶體驗。如果我們能把不同路由對應的組件分割成不同的代碼塊,然后當路由被訪問的時候才加載對應的組件,這樣就更加高效。對於Vue
路由懶加載的方式有Vue
異步組件、動態import
、webpack
提供的require.ensure
,最常用的就是動態import
的方式。
{
path: "/example",
name: "example",
//打包后,每個組件單獨生成一個chunk文件
component: () => import("@/views/example.vue")
}
服務端渲染SSR
如果需要優化首屏加載速度並且首屏加載速度是至關重要的點,那么就需要服務端渲染SSR
,服務端渲染SSR
其實是優缺點並行的,需要合理決定是否真的需要服務端渲染。
優點
- 更好的
SEO
,由於搜索引擎爬蟲抓取工具可以直接查看完全渲染的頁面,如果SEO
對站點至關重要,而頁面又是異步獲取內容,則可能需要服務器端渲染SSR
解決此問題。 - 更快的內容到達時間
time-to-content
,特別是對於緩慢的網絡情況或運行緩慢的設備,無需等待所有的JavaScript
都完成下載並執行,用戶將會更快速地看到完整渲染的頁面,通常可以產生更好的用戶體驗,並且對於那些內容到達時間time-to-content
與轉化率直接相關的應用程序而言,服務器端渲染SSR
至關重要。
缺點
- 開發條件所限,瀏覽器特定的代碼,只能在某些生命周期鈎子函數
lifecycle hook
中使用,一些外部擴展庫external library
可能需要特殊處理,才能在服務器渲染應用程序中運行。 - 涉及構建設置和部署的更多要求,與可以部署在任何靜態文件服務器上的完全靜態單頁面應用程序
SPA
不同,服務器渲染應用程序,通常需要處於Node.js server
運行環境。 - 更大的服務器端負載,在
Node.js
中渲染完整的應用程序,顯然會比僅僅提供靜態文件的server
更加大量占用CPU
資源CPU-intensive
-CPU
密集型,因此如果預料在高流量環境high traffic
下使用,需要准備相應的服務器負載,並明智地采用緩存策略。
使用keep-alive組件
當在組件之間切換的時候,有時會想保持這些組件的狀態,以避免反復重渲染導致的性能等問題,使用<keep-alive>
包裹動態組件時,會緩存不活動的組件實例,而不是銷毀它們。重新創建動態組件的行為通常是非常有用的,但是在有些情況下我們更希望那些標簽的組件實例能夠被在它們第一次被創建的時候緩存下來,此時使用<keep-alive>
包裹組件即可緩存當前組件實例,將組件緩存到內存,用於保留組件狀態或避免重新渲染,和<transition>
相似它,其自身不會渲染一個DOM
元素,也不會出現在組件的父組件鏈中。
<keep-alive>
<component v-bind:is="currentComponent" class="tab"></component>
</keep-alive>
打包優化
模板預編譯
當使用DOM
內模板或JavaScript
內的字符串模板時,模板會在運行時被編譯為渲染函數,通常情況下這個過程已經足夠快了,但對性能敏感的應用還是最好避免這種用法。預編譯模板最簡單的方式就是使用單文件組件——相關的構建設置會自動把預編譯處理好,所以構建好的代碼已經包含了編譯出來的渲染函數而不是原始的模板字符串。如果使用webpack
,並且喜歡分離JavaScript
和模板文件,可以使用vue-template-loader
,其可以在構建過程中把模板文件轉換成為JavaScript
渲染函數。
SourceMap
在項目進行打包后,會將開發中的多個文件代碼打包到一個文件中,並且經過壓縮、去掉多余的空格、babel
編譯化后,最終將編譯得到的代碼會用於線上環境,那么這樣處理后的代碼和源代碼會有很大的差別,當有bug
的時候,我們只能定位到壓縮處理后的代碼位置,無法定位到開發環境中的代碼,對於開發來說不好調式定位問題,因此sourceMap
出現了,它就是為了解決不好調式代碼問題的,在線上環境則需要關閉sourceMap
。
配置splitChunksPlugins
Webpack
內置了專門用於提取多個Chunk
中的公共部分的插件CommonsChunkPlugin
,是用於提取公共代碼的工具,CommonsChunkPlugin
於4.0
及以后被移除,使用SplitChunksPlugin
替代。
使用treeShaking
tree shaking
是一個術語,通常用於描述移除JavaScript
上下文中的未引用代碼dead-code
,其依賴於ES2015
模塊系統中的靜態結構特性,例如import
和export
,這個術語和概念實際上是興起於ES2015
模塊打包工具rollup
。
第三方插件的按需引入
我們在項目中經常會需要引入第三方插件,如果我們直接引入整個插件,會導致項目的體積太大,我們可以借助babel-plugin-component
,然后可以只引入需要的組件,以達到減小項目體積的目的,以項目中引入element-ui
組件庫為例。
{
"presets": [["es2015", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
import Vue from 'vue';
import { Button, Select } from 'element-ui';
Vue.use(Button)
Vue.use(Select)
每日一題
https://github.com/WindrunnerMax/EveryDay
參考
https://zhuanlan.zhihu.com/p/121000054
https://www.jianshu.com/p/f372d0e3de80
https://juejin.im/post/6844903887787278349
https://juejin.im/post/6844904189999448071
https://www.lagou.com/lgeduarticle/22278.html
https://www.cnblogs.com/mmzuo-798/p/11778044.html
https://blog.csdn.net/gtlbtnq9mr3/article/details/104889927