我是如何在 Vue 項目中作代碼分割的

一般爲了開發效率,咱們會使用 vue-cli 建立項目,這樣建立的項目默認狀況下編譯是會對代碼進行分割的。可是若是是自行配置的 webpack 環境的話,仍是頗有必要熟悉代碼分割的相關知識的。css

爲何要作代碼分割

在配置 webpack 的過程當中,不少時候咱們的 webpack 入口只寫了一個 entry: '${sourceDir}/index.js’,默認狀況下只會生成一個 bundle 文件,包含了第三方庫、公共代碼及不一樣頁面所用到的業務邏輯,這必然會形成該 bundle 文件體積過大,影響頁面首次的加載速度,所以咱們須要對代碼進行分割,加快首次進入頁面的速度。html

代碼分割思路

首先把第三方庫、公共代碼抽離出來,由於這些代碼變更的頻率小,能夠打包成一個文件,這樣每次上線文件都不發生變化,能夠充分利用網絡緩存加快文件下載速度,分割的細的話就是,第三方庫爲一個 js 文件, 公共代碼爲一個 js 文件。vue

而後,按照路由(頁面)進行代碼分割,每一個頁面生成一個 js 文件,這樣每次首次進入就只加載公共代碼和本頁面用的的 js 文件, 而不用加載其它頁面無關的代碼。node

最後,再進行精細分割的話,就是根據組件使用狀況進行分割,來實現組件的懶加載,好比:頁面中的不一樣 tab,能夠根據 tab 的展現狀況進行分割,把須要點擊或者用戶主動操做才能呈現的組件進行懶加載,這樣就在頁面級又進行了更細粒度的代碼分割。webpack

代碼分割實戰

第三方庫及公共代碼分割

第一步咱們進行第三方庫的分割,好比 vue、vue-router、vuex、axios 等三方庫,把它們放到 vender.js 中,而後 utils、common 文件等放在 common.js 中。這些經過 webpack 的 entry 及 splitChunk 配置便可實現。ios

修改 entry 配置:web

{
  // ...
  entry: {
    // 把公共代碼放到 common 裏
    common: [`${sourceDir}/utils/index.js`],
    main: `${sourceDir}/index.js`,
  },
  // ...
}

splitChunk 配置:vue-router

{
  optimization: {
    // splitChunks 配置
    splitChunks: {
      cacheGroups: {
        default: {
          name: 'vendor',
          // 把第三方庫放到 vendor 裏,包括 vue, vue-router, vuex 等
          // 由於他們都是從 node_modules 里加載的,這裏直接正則匹配
          test: /[\\/]node_modules[\\/]/,
          chunks: 'initial',
          // 調整優先級,優先處理
          priority: 10,
        },
        common: {
          chunks: 'all',
          name: 'common',
          // 匹配 entry 裏的 common 配置
          test: 'common',
        },
      },
    },
      // runtime 代碼放在 runtime 文件中
    runtimeChunk: {
      name: 'runtime',
    },
  }
}

另外就是 output 配置了,[name] 表示讓 chunk 名稱做爲文件名, [chunkhash:8] 表示加上 hash,上線後不走緩存加載最新的代碼。vuex

{
  output: {
    path: path.join(__dirname, './dist'),
    filename: 'static/[name].[chunkhash:8].bundle.js',
    chunkFilename: 'static/[name].[chunkhash:8].bundle.js',
  },
}

作完第三方庫及公共代碼分割,打包後生成的文件以下:vue-cli

assets by path static/*.js 138 KiB
  asset static/vendor.4391b08b.bundle.js 133 KiB [emitted] [immutable] [minimized] (name: vendor) (id hint: default)
  asset static/main.0d6dab3a.bundle.js 3.9 KiB [emitted] [immutable] [minimized] (name: main)
  asset static/runtime.bdaa3432.bundle.js 1.1 KiB [emitted] [immutable] [minimized] (name: runtime)
  asset static/common.3f62940b.bundle.js 204 bytes [emitted] [immutable] [minimized] (name: common)
asset index.html 537 bytes [emitted]
asset static/main.acdc2841.bundle.css 127 bytes [emitted] [immutable] [minimized] (name: main)

咱們能夠看到代碼分割到了不一樣的文件中,vender.js 包含了全部的第三方庫,main.js 包含了咱們各個頁面的業務邏輯,公共代碼在 common 中,runtime 包含了運行時代碼,這樣代碼就分散到了不一樣的文件中,各司其職,且有利於同時進行加載。

可是 main.js 仍是包含了多個頁面的代碼,若是隻是進入首頁的話,其它頁面的代碼就是多餘的,接下來再進行優化。

按路由分割

這一個比較容易處理,只需改變下路由配置便可,以 () => import(path) 的方式加載頁面組件:

const routes = [
  {
    path: '/',
    // component: Home,
    component: () => import('./pages/Home'),
  },
  {
    path: '/todos',
    // component: Todos,
    component: () => import('./pages/Todos'),
  },
  {
    path: '/about',
    // component: About,
    component: () => import('./pages/About'),
  },
  {
    path: '/404',
    // component: NotFound,
    component: () => import('./pages/NotFound'),
  },
  {
    path: '*',
    redirect: '/404',
  },
];

此時打包會看到多了不少文件,這是把不一樣頁面的代碼分割到了不一樣的 JS 文件中,只有訪問對應的頁面纔會加載相關的代碼。

assets by path static/*.js 142 KiB
  asset static/vendor.4391b08b.bundle.js 133 KiB [emitted] [immutable] [minimized] (name: vendor) (id hint: default)
  asset static/runtime.07c35c52.bundle.js 3.99 KiB [emitted] [immutable] [minimized] (name: runtime)
  asset static/821.7ba5112d.bundle.js 1.89 KiB [emitted] [immutable] [minimized]
  asset static/main.1697fd27.bundle.js 1.68 KiB [emitted] [immutable] [minimized] (name: main)
  asset static/820.de28fd7b.bundle.js 562 bytes [emitted] [immutable] [minimized]
  asset static/646.a902d0eb.bundle.js 406 bytes [emitted] [immutable] [minimized]
  asset static/114.26876aa2.bundle.js 402 bytes [emitted] [immutable] [minimized]
  asset static/common.3f62940b.bundle.js 204 bytes [emitted] [immutable] [minimized] (name: common)
assets by path static/*.css 127 bytes
  asset static/main.beb1183a.bundle.css 75 bytes [emitted] [immutable] [minimized] (name: main)
  asset static/821.cd9a22a5.bundle.css 52 bytes [emitted] [immutable] [minimized]
asset index.html 537 bytes [emitted]

固然,這個地方可能會有爭議,爭議的地方就是:「頁面進入時就把全部頁面的代碼都下載下來,再進入其它頁面不是更快嗎?」。這就取決於項目狀況了,看是着重於頁面秒開,仍是着重於頁面切換體驗。若是着重於秒開的話,配合 SSR 處理效果會更好。

更細粒度的分割

若是對於頁面打開速度或性能有更高的要求,還能夠作更細粒度的代碼分割,好比頁面中功能模塊的懶加載。

這裏以一個點擊按鈕時加載相應的組件爲例,進行代碼演示:

這裏有一個 Load Lazy Demo 按鈕,點擊時才加載 LazyComponent 組件,LazyComponent 組件並無什麼特別之處,寫法跟普通組件同樣。

<template>
  <button @click="loadLazyDemo">Load Lazy Demo</button>
  <template v-if="showLazyComponent">
    <lazy-component />
  </template>
</template>

這裏經過一個 showLazyComponent 控制組件的顯示,當點擊按鈕時,把 showLazyComponent 置爲 true,而後就加載 LazyComponent 對應的代碼了。其實關鍵仍是經過 () => import(path) 的方式引入組件。

<script>
export default {
  data() {
    return {
      showLazyComponent: false,
    };
  },
  methods: {
    loadLazyDemo() {
      this.showLazyComponent = true;
    },
  },
  components: {
    'lazy-component': () => import('../components/LazyComponent'),
  },
};
</script>

OK,以上就是我在 Vue 項目中作的代碼分割的相關內容。

相關文章
相關標籤/搜索