vue效能優化小結
元件優化
一般來說,你不需要太關心vue的執行時效能,它在執行時非常快,但付出的代價是初始化時相對較慢。先看一下常見的vue寫法:在html裡放一個app元件,app元件裡又引用了其他的子元件,形成一棵以app為根節點的元件樹。
<div id="app">
<router-view></router-view>
</div>
複製程式碼
而正是這種做法引發了效能問題,要初始化一個父元件,必然需要先初始化它的子元件,而子元件又有它自己的子元件。那麼要初始化根標籤,就需要從底層開始冒泡,將頁面所有元件都初始化完。所以我們的頁面會在所有元件都初始化完才開始顯示。
這個結果顯然不是我們要的,更好的結果是頁面可以從上到下按順序流式渲染,這樣可能總體時間增長了,但首屏時間縮減,在使用者看來,頁面開啟速度就更快了。
非同步元件
new Vue({
components: {
A: { /*component-config*/ },
B (resolve) {
setTimeout(() => {
resolve({ /*component-config*/ })
}, 0);
}
}
})
複製程式碼
或者是
const Foo = resolve => {
// require.ensure 是 Webpack 的特殊語法,用來設定 code-split point
// (程式碼分塊)
require.ensure(['./Foo.vue' ], () => {
resolve(require('./Foo.vue'))
})
}
const Foo = resolve => require(['./Foo.vue'], resolve)
const router = new VueRouter({
routes: [
{ path: '/foo', component: Foo }
]
})
複製程式碼
把不同路由對應的元件分割成不同的程式碼塊,然後當路由被訪問的時候才載入對應元件,從而實現路由懶載入
把元件按組分塊
有時候我們想把某個路由下的所有元件都打包在同個非同步 chunk 中。只需要 給 chunk 命名,提供 require.ensure 第三個引數作為 chunk 的名稱:
const Foo = r => require.ensure([], () => r(require('./Foo.vue')), 'group-foo')
const Bar = r => require.ensure([], () => r(require('./Bar.vue')), 'group-foo')
const Baz = r => require.ensure([], () => r(require('./Baz.vue')), 'group-foo')
複製程式碼
利用v-if和terminal
<head>
<!--some component -->
<div v-if="showB">
<!--some component -->
</div>
<div v-if="showC">
<!--some component -->
</div>
</head>
data: {
showB: false,
showC: false
},
created () {
// 顯示B
setTimeout(() => {
this.showB = true;
}, 0);
// 顯示C
setTimeout(() => {
this.showC = true;
}, 0);
}
複製程式碼
這個示例寫起來略顯囉嗦,但它已經實現了我們想要的順序渲染的效果。頁面會在元件初始化完後顯示,然後再按順序渲染其餘的元件,整個頁面渲染方式看起來是流式的。
有些人可能會擔心v-if存在一個編譯/解除安裝過程,會有效能影響。但這裡並不需要擔心,因為v-if是惰性的,只有當第一次值為true時才會開始初始化。
元件keep-alive
如果你做用一個大型web的spa的時候,你有很多router,對應的是很多個頁面。在頁面的快速切換中(如常見的tab頁),為了保證頁面載入的效率,除了快取機制之外,vue的keep-alive元件可以幫的上忙。它會把元件儲存在瀏覽器記憶體當中,方便你快速切換。
基礎優化
v-if v-show
許可權問題,只要涉及到許可權相關的展示無疑要用 v-if,沒有許可權限制下根據使用者點選的頻次選擇,頻繁切換的使用 v-show,不頻繁切換的使用 v-if,這裡要說的優化點在於減少頁面中 dom 總數,我比較傾向於使用 v-if,因為減少了 dom 數量,加快首屏渲染。 不要在模板裡面寫過多的表示式與判斷 v-if="isShow && isAdmin && (a || b)",這種表示式雖說可以識別,但是不是長久之計,當看著不舒服時,適當的寫到 methods 和 computed 裡面封裝成一個方法,這樣的好處是方便我們在多處判斷相同的表示式,其他許可權相同的元素再判斷展示的時候呼叫同一個方法即可。
迴圈儘可能在使用 v-for 時提供 key,除非遍歷輸出的 DOM 內容非常簡單,或者是刻意依賴預設行為以獲取效能上的提升。引用vue文件:當 Vue.js 用 v-for 正在更新已渲染過的元素列表時,它預設用“就地複用”策略。如果資料項的順序被改變,Vue 將不會移動 DOM 元素來匹配資料項的順序, 而是簡單複用此處每個元素,並且確保它在特定索引下顯示已被渲染過的每個元素。這個類似 Vue 1.x 的 track-by="$index" 。
這個預設的模式是高效的,但是隻適用於不依賴子元件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出。
為了給 Vue 一個提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要為每項提供一個唯一 key 屬性。理想的 key 值是每項都有的唯一 id。 watch 和 computed 用哪個的問題看官網的例子,計算屬性主要是做一層 filter 轉換,切忌加一些呼叫方法進去,watch 的作用就是監聽資料變化去改變資料或觸發事件如 this.$store.dispatch('update', { ... }) 資料請求在哪個時候儘量根據需求考慮後,多多利用promise的併發請求。
其他
如打包優化:在打包時可將一些靜態模組排除,如ue、vuex、vue-router、axios 等,換用國內的 bootcdn 直接引入到根目錄的 index.html 中。在 webpack 裡有個 externals,可以忽略不需要打包的庫
externals: {
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios': 'axios'
}
複製程式碼
或者是利用webpack的動態連結庫DllPlugin,打包會輸出一個類dll包(dll包源於windows的動態連結庫),這些程式碼本身不會執行,主要是提供給我們的業務程式碼引用。將靜態資原始檔(執行依賴包)與原始檔分開打包,先使用DllPlugin給靜態資源打包,再使用DllReferencePlugin讓原始檔引用資原始檔。dll在打包一次後即可,下次業務程式碼修改也不會重新打包,然後在html裡分模組引入
<script src="./static/js/vendor.dll.js"></script>
<script src="/dist/build.js"></script>
複製程式碼
關於webpack的使用建議閱讀《深入淺出webpack》一書。 其他的還有樣式優化、減少元件耦合性等。