『翻譯』基於 Vue.js 與 Webpack 的三種程式碼分割正規化

FREAKFILTH發表於2017-09-15

原文連結:3 Code Splitting Patterns For VueJS and Webpack

前言

程式碼分割是提升單頁應用初始載入速度的重要方式之一。因為使用者不用在第一次進入應用時下載所有程式碼,使用者能更快的看到頁面並與之互動。這會改善使用者體驗,尤其在移動端;而且這對 SEO 有很大幫助,因為 Google 會降低載入速度慢的網站權重。

上週我寫了一篇關於Vue.js 與 Webpack 如何分割程式碼的文章,長話短說,每個元件都封裝在單個檔案中,那很容易分割程式碼,當你匯入模組時,Webpack 可以建立一個分割點,並且 Vue 也可以很方便的載入一個非同步元件。

我認為程式碼分割最困難的部分不是如何讓它工作起來,而是何時何地讓它工作。我想說,當設計你的應用時,就要將程式碼分割作為架構考慮進去。

在這篇文章中,我將介紹目前 Vue.js 的三種程式碼分割方式:

  • By page(按照頁面切分)

  • By page fold(按照頁面的可見區域摺疊切分)
    sxsa

  • By condition(按條件載入)

注:這篇文章最初於2017/07/08發表在Vue.js開發部落格上。

1.By page(按照頁面切分)

按照頁面切分是思路最清晰的。這個簡單的應用有三個頁面:

我們假設每個元件都是一個單獨的檔案,比如:Home.vue, About.vueContact.vue,然後我們可以使用 Webpack 的動態 import(dynamic import) 功能拆分成單獨的構建檔案。當使用者訪問不同頁面時,Wenpack 會非同步載入並請求改頁檔案。

如果你使用 vue-router,這很容易實現,因為你的頁面已經在單獨的元件裡了。

const Home = () => import(/* webpackChunkName: "home" */ './Home.vue');
const About = () => import(/* webpackChunkName: "about" */ './About.vue');
const Contact = () => import(/* webpackChunkName: "contact" */ './Contact.vue');
const routes = [
  { path: '/', name: 'home', component: Home },
  { path: '/about', name: 'about', component: About },
  { path: '/contact', name: 'contact', component: Contact }
];複製程式碼

看看我們編譯程式碼時的統計資料,每個頁面都在它們自己的檔案裡,但要注意到有個重要的bundle檔案叫 build_main.js,它包含了所有的公共程式碼以及非同步載入其他檔案的邏輯。無論使用者訪問哪個路由,都必須先載入它。

現在我訪問 http://localhost:8080/#/contact 載入 Contact 頁面,我檢視 Network 選單,發現下列檔案被載入:

注意 build_main.js 這一欄的 initiator 值為 (index)。這意味著 index.html 請求了這個指令碼,這正是我們所期盼的。但是 build_1.jsinitiator 卻是 bootstrap_a877…,這是 Webpack 指令碼負責的非同步載入檔案。當你使用 Webpack 的動態匯入功能,這個指令碼會自動加入構建。最重要的一點是: build_1.js 不會阻塞初始頁面的載入。

2.By page fold(按照頁面的可見區域摺疊切分)

摺疊以下(Below the “fold”)代表頁面初始時不可見的部分。你可以非同步載入這些內容,因為使用者通常需要一兩秒鐘才能閱讀完摺疊以上的內容,尤其是在第一次訪問站點時。

在這個例項應用中,我考慮把摺疊線設在刊頭下。那麼讓我們在頁面初始化時載入導航欄和刊頭,它們之下的所有內容,稍後再載入。我會建立一個名叫 BelowFold 的元件,提取出相關的程式碼如下:

Home.vue:

<template>
  <div>
    <div class="jumbotron">
        <h1>Jumbotron heading</h1>
        ...
    </div>
    <below-fold></below-fold>
    <!--All the code below here has been put into-->
    <!--into the above component-->
    <!--<div class="row marketing">
      <div class="col-lg-6">
        <h4>Subheading</h4>
        <p>Donec id elit non mi porta gravida at eget metus. Maecenas faucibus mollis interdum.</p>
        ...
      </div>
      ...
    </div>-->
  </div>
</template>
<script>

  const BelowFold = () => import(
    /* webpackChunkName: "below-fold" */ './BelowFold.vue'
  );
  export default {
    ...
    components: {
        BelowFold
    }
  }
</script>複製程式碼

BelowFold.vue:

<template>
  <div class="row marketing">
    <div class="col-lg-6">
      <h4>Subheading</h4>
      <p>Donec id elit non mi porta gravida at eget metus. Maecenas faucibus mollis interdum.</p>
      ...
    </div>
    ...
  </div>
</template>複製程式碼

當我們編譯程式碼時,可以看到 below-fold 被打包成了單獨的檔案:

提示:below-fold 小到只有1.36k,看起來似乎不值得把它單獨分離出來。因為現在只是一個很小的演示應用。在真實的應用中,頁面的大部分內容都在摺疊以下,因此可能有大量的程式碼,它包括 JSCSS 以及所有子元件。

3.By condition(按條件載入)

另一個選擇方案是按條件載入。比如:模態框、Tab頁、選單等。

這個應用有個模態框,當你按下"Sign up today"按鈕時會彈出它:

和之前一樣,我們只是將模態框程式碼移動到它自己的單個檔案元件中:

Home.vue:

<template>
  <div>
    <div class="jumbotron">...</div>
    <below-fold></below-fold>

    <home-modal v-if="show" :show="show"></home-modal>
  </div>
</template>
<script>
  const BelowFold = () => import(
    /* webpackChunkName: "below-fold" */ './BelowFold.vue'
  );
  const HomeModal = () => import(
    /* webpackChunkName: "modal" */ './HomeModal.vue'
  );

  export default {
    data() {
      return {
        show: false
      }
    },
    components: {
      HomeModal,
      BelowFold
    }
  }
</script>複製程式碼

HomeModal.vue:

<template>
    <modal v-model="show" effect="fade">...</modal>
</template>
<script>
  import Modal from 'vue-strap/src/Modal.vue';
  export default {
    props: ['show'],
    components: {
        Modal
    }
  }
</script>複製程式碼

注意我在模態框上加了 v-if。布林值 show 用來開啟/關閉模態框,並且它也用來判斷是否渲染模態框本身。因為初始化頁面時 showfalse,只有當模態框開啟時,才會下載程式碼。

這很合適,因為如果使用者沒有開啟模態框,那這塊程式碼是不會下載的。唯一的缺點是:它有很小的使用者體驗成本,當使用者按下按鈕後必須等待檔案下載完成。

再次編譯,下面是現在的輸出結果:

啊哈,我們又節省了5KB的首屏流量...

結論

除了以上三種程式碼分割的方法,我相信一定還有其他方法去實現,只要你運用自己的想象力!

本文譯者:餘震(Freak)
譯文出處:Rockjins Blog
版權宣告:本部落格所有文章除特別宣告外,均採用 CC BY-NC-SA 3.0 CN許可協議。轉載請註明出處!

相關文章