前言
我們清楚,在 webpack
中通過CommonsChunkPlugin
可以將 entry
的入口檔案中引用多次的檔案抽離打包成一個公用檔案,從而減少程式碼重複冗餘
entry: {
main: `./src/main.js`,
user: `./src/user.js`
},
......
new webpack.optimize.CommonsChunkPlugin({
name: "commons",
filename: `common.js`,
minChunks: 2,
})
// 打包生成一個檔案common.js ,包含main.js 和 user.js 中引用兩次及以上的模組程式碼
那麼問題來了,當使用了類似 vue-router
的程式碼分割+懶載入功能的時候,每個路由對應的.vue
檔案中,共同引用了多次的模組,要怎麼抽離出程式碼分割模組的公用模組程式碼出來呢?
問題實際場景
舉個例子
// 懶載入路由檔案 routes.js
const
Index = () => import(/* webpackChunkName: "index" */ "page/index/Index.vue"),
User = () => import(/* webpackChunkName: "userIndex" */ "page/user/Index.vue"),
UserDetail = () => import(/* webpackChunkName: "userDetail" */ "page/user/Detail.vue"),
...
// page/index/Index.vue 首頁路由檔案
<template>首頁</template>
<script>
import pub from `script/public.js`
...
</script>
// page/index/Index.vue 使用者頁路由檔案
<template>使用者頁</template>
<script>
import pub from `script/public.js`
...
</script>
上述使用了vue-router
懶載入打包出來的 首頁路由檔案index.js
和 使用者頁檔案userIndex.js
都會包含一份 public.js
的程式碼,重複了。
那麼問題就是,在程式碼分割的程式碼中,怎麼自動抽離公共程式碼? 就像CommonsChunkPlugin
的效果一樣,CommonsChunkPlugin
怎麼在 code-splitting
的場景上使用呢 ?
解決方案
如問題所示,存在兩個使用了webpack code-splitting 和 懶載入的路由檔案,路由檔案都使用了公用的public.js
模組。
// page/index/Index.vue 首頁路由檔案
<template>首頁</template>
<script>
import pub from `script/public`
...
</script>
// 使用者頁
// page/index/Index.vue 使用者頁路由檔案
<template>使用者頁</template>
<script>
import pub from `script/public`
...
</script>
要將 public.js
公用模組抽離,有三種解決方案
方案一,CommonsChunkPlugin
具名模組
手動將所有共用的模組抽離在一個檔案。
建立檔案commons.js
// commons.js
import pub from `public`
在webpack.config.js
的CommonsChunkPlugin
外掛指定commons
的entry
// webpack.config.js
entry:{
main: `src/main.js`,
commons: `src/commons.js`
},
...
new webpack.optimize.CommonsChunkPlugin({
name: "commons", // 和 entry的commons對應,
filename: `common.bundle.js`, // 抽離公共檔案
minChunks: Infinity,
})
這樣,如果路由檔案或其他模組使用到了 commons.js
中的模組,都不會重複載入程式碼,而是在common.bundle.js
中獲取。
方案二,CommonsChunkPlugin
設定 children
屬性
官方文件CommonsChunkPlugin 中 children屬性解釋
Move common modules into the parent chunk
With Code Splitting, multiple child chunks of an entry chunk can have common dependencies. To prevent duplication these can be moved into the parent. This reduces overall size, but does have a negative effect on the initial load time. If it is expected that users will need to download many sibling chunks, i.e. children of the entry chunk, then this should improve load time overall.
可知,設定 children 為 true 可以將code-splitting的模組的依賴模組抽離到父模組,這樣做的後果就是,確實抽離公用模組,降低了程式碼重複,減少了程式碼體積。但是同時,抽離到父模組,也意味著如果有一個懶載入的路由 ShopList.vue
沒有用到public.js
模組,但是實際上引入了父模組,也為這ShopList.vue
也引入了public.js
的程式碼。
這就需要CommonsChunkPlugin
的 async
屬性。
方案三(最佳實踐),children
與 async
雙管齊下
Extra async commons chunk
Similar to the above one, but instead of moving common modules into the parent (which increases initial load time) a new async-loaded additional commons chunk is used. This is automatically downloaded in parallel when the additional chunk is downloaded.
設定了async
, 會將上述懶載入的路由檔案公用的模組程式碼,抽離打包成一個單獨的檔案,並且該檔案是按需載入的,如果某個路由沒有使用到這些公用模組,是不會載入進來的。
舉個例子:
首頁路由模組(訪問路徑/index
),引用了 public
模組
使用者路由模組(訪問路徑/user
),引用了 public
模組
購物車模組(訪問路徑/shop
),沒有引用 public
模組
那麼,打包生成的檔案大概是
main.js - 根入口檔案
index.js - 首頁路由檔案
user.js - 使用者路由檔案
shop.js - 購物車路由檔案
0.js - 抽離路由的公用模組檔案
訪問url/index
,載入的依賴檔案是main.js + index.js + 0.js
訪問url/user
,載入的依賴檔案是main.js + user.js + 0.js
訪問url/shop
,載入的依賴檔案是main.js + shop.js
基本解決了 lazy load + code-splitting 情況下的公用模組抽離。
以下附上簡單的webpack.config.js
配置程式碼
entry: {
main: `./src/main.js`
},
...
plugins: [
...
new webpack.optimize.CommonsChunkPlugin({
name: "main",
minChunks: 2,
children: true,
// deepChildren: true,
async: true,
})
]
The CommonsChunkPlugin has been removed in webpack v4 legato. To learn how chunks are treated in the latest version, check out the SplitChunksPlugin.
PS: webpack 4 已經將
CommonsChunkPlugin
廢棄,解決方案僅能在webpack 3 以下使用。
參考資料
commons-chunk-plugin
CommonChunkPlugin: Feature – Select statically imported modules from chunks that were created from a dynamic import (require.ensure / System.import / import(“..”))