vue效能優化小結

JayJunG發表於2018-12-17

元件優化

一般來說,你不需要太關心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》一書。 其他的還有樣式優化、減少元件耦合性等。


相關文章