代碼分割與懶加載狀況下(code-splitting+lazyload)抽離懶加載模塊的公用模塊代碼

前言

咱們清楚,在 webpack 中經過CommonsChunkPlugin 能夠將 entry 的入口文件中引用屢次的文件抽離打包成一個公用文件,從而減小代碼重複冗餘vue

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文件中,共同引用了屢次的模塊,要怎麼抽離出代碼分割模塊的公用模塊代碼出來呢?webpack

問題實際場景

舉個栗子git

// 懶加載路由文件 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的代碼,重複了。github

那麼問題就是,在代碼分割的代碼中,怎麼自動抽離公共代碼? 就像CommonsChunkPlugin的效果同樣,CommonsChunkPlugin怎麼在 code-splitting 的場景上使用呢 ?web

解決方案

如問題所示,存在兩個使用了webpack code-splitting 和 懶加載的路由文件,路由文件都使用了公用的public.js模塊。vue-router

// 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公用模塊抽離,有三種解決方案async

方案一,CommonsChunkPlugin 具名模塊

手動將全部共用的模塊抽離在一個文件。
建立文件commons.jsui

// commons.js
import pub from 'public'

webpack.config.jsCommonsChunkPlugin插件指定commons 的entrythis

// 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中獲取。url

方案二,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的代碼。

這就須要CommonsChunkPluginasync 屬性。

方案三(最佳實踐),childrenasync 左右開弓

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(".."))

相關文章
相關標籤/搜索