Webpack:代碼分割

webpack 版本:4.35.0前端

webpack-cli 版本:3.3.5node

爲何須要代碼分割

在當前的前端項目中,經常使用 gulp、webpack、Browserify 等將多個文件壓縮合併成一個。這個過程稱爲打包。 打包是一個將文件引入併合併到一個單獨文件的過程,最終造成一個 「bundle」。接着在頁面上引入該 bundle,整個應用便可一次性加載。webpack

打包是個很是棒的技術,但隨着你的應用增加,你的代碼包也將隨之增加。尤爲是在整合了體積巨大的第三方庫的狀況下。你須要關注你代碼包中所包含的代碼,以免因體積過大而致使加載時間過長。web

爲了不搞出大致積的代碼包,在前期就思考該問題並對代碼包進行分割是個不錯的選擇。代碼分割是由諸如 Webpack(代碼分割)和 Browserify(factor-bundle)這類打包器支持的一項技術,可以建立多個包並在運行時動態加載。算法

對應用進行代碼分割可以幫助你「懶加載」當前用戶所須要的內容,可以顯著地提升你的應用性能。儘管並無減小應用總體的代碼體積,但你能夠避免加載用戶永遠不須要的代碼,並在初始加載的時候減小所需加載的代碼量。gulp

關鍵點

打包解決了項目中依賴文件過多,而致使 http 請求過多的問題瀏覽器

代碼分割則是解決打包的反作用——單文件過大,致使加載時間過長。緩存

所以代碼分割的關鍵點在於,如何控制分割粒度bash

  • 分割文件多少
  • 文件大小

分割方法

有三種經常使用的代碼分離方法:babel

  • 入口起點:使用 entry 配置手動地分離代碼。
  • 動態導入:經過模塊的內聯函數調用來分離代碼。
  • 使用插件手動分離

entry

每一個 entry 都會在最後的打包中輸出一個對應的文件。所以能夠顯示地進行代碼分割:

module.exports = {
    entry: {
        app: './src/index.js',
        print: './src/print.js'
    },
    // ...
    output: {
        filename: '[name].[chunkhash].js',
        path: path.resolve(__dirname, './dist')
    }
}
複製代碼

進行打包構建後會在 dist 文件夾輸出:

app.f7badf83cc90de106d41.js
print.e00bba994e917cc7fc59.js
複製代碼

動態導入

動態導入也能夠達到代碼分割的效果:

import _ from 'lodash';

function component() {
  var element = document.createElement('div');

  // Lodash(目前經過一個 script 腳本引入)對於執行這一行是必需的
  element.innerHTML = _.join(['你好呀', 'webpack'], ' ');

  element.onclick = (e) => import(/* webpackChunkName: "print" */ './print').then((Print) => {
  })

  return element;
}

document.body.appendChild(component());

複製代碼

webpackChunkName 設置了輸出包的名字。

module.exports = {
    entry: {
        app: './src/index.js'
    },
    // ...
    output: {
        filename: '[name].[chunkhash].js',
        chunkFilename: '[name].[chunkhash].js',
        path: path.resolve(__dirname, './dist')
    }
}
複製代碼

outputchunkFilename 決定了非入口(non-entry) chunk 文件的名稱。這裏的 chunk 是經過代碼分割產生的,所以這裏經過此選項來設置文件的名字。

運行打包:

app.73efde30ee057cb8c817.js
print.e4408db3b4e5072295de.js
複製代碼

插件

CommonsChunkPlugin

在 webpack 4 以前,使用 CommonsChunkPlugin 進行公共 chunk 抽取。

module.exports = {
    entry: './src/index.js',
    entry: {
      main: './src/index.js',
      vendor: [
        'lodash'
      ]
    },
  plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
      title: 'Caching'
    }),
      new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor'
      }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest'
    })
  ],
  output: {
    filename: '[name].[chunkhash].js',
    path: path.resolve(__dirname, 'dist')
  }
};
複製代碼

這裏有兩點值得注意:

  • entry 中的 vendor:顯式引用 lodash,因爲 index.js 也導入了 lodash,所以它就變成了一個公共的模塊。
  • 使用 new webpack.optimize.CommonsChunkPlugin 抽取這些公共模塊

SplitChunksPlugin

webpack 4 以後 CommonsChunkPlugin 被棄用了,使用 SplitChunksPlugin 代替。 SplitChunksPlugin 是開箱即用的,只須要在 module.exports 中進行配置:

module.exports = {
  //...
  optimization: {
    splitChunks: { // 該選項將用於配置 SplitChunksPlugin 插件
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor',
          chunks: 'initial',
          minChunks: 2
        }
      }
    }
  },
  output: {
    filename: '[name].[chunkhash].js',
    chunkFilename: '[name].[chunkhash].js',
    path: path.resolve(__dirname, './dist')
  }
};
複製代碼

上面的配置會將 node_modules 下引用的全部模塊都使用 vendor 這個做爲 chunk 名以 outputchunkFilename 設置的格式打包到 dist 文件夾中。

SplitChunksPlugin 的選項:

cacheGroups 中每一項都接收以下參數:

  • filename: 重寫 chunk 命名方式

    vendors: {
      test: /[\\/]node_modules[\\/]/,
      name: 'vendor',
      filename:'[name].bundle.js'
      chunks: 'initial',
      minChunks: 2,
    }
    複製代碼

    最後會生成名字爲 vendor.bundle.js 的文件

    關於 SplitChunksPlugin 插件的配置,請參見 SplitChunksPlugin 解析

分割內容

代碼分割的關鍵點在於控制分割粒度

  • 分割文件多少
  • 文件大小

若是在不考慮大小的狀況下,最後會打包出包含全部代碼的單個文件。咱們能夠 optimization.splitChunks.maxSize 來控制 chunk 的大小以達到分割成多文件的目的。

只經過大小來控制代碼分割擁有以下弊端:

  • 入口文件大小未獲得最大的優化

  • 不能使用緩存。

    在項目中,有些引入的工具的更新頻率是很是低的,這時候

  • 表現和結構未進行分離。

    瀏覽器進行渲染的時候,表現和結構是基於不一樣的算法進行解析的。而且,樣式代碼也會增長請求的響應時間。

三方庫

在項目中,經常會引入一些三方的工具庫或者 polyfill 等,好比 lodashbabel-polyfill。這些代碼在項目中改動不多,所以建議將這些代碼提取到一個單獨的文件中。

這樣作有幾個好處:

  • 能夠進行緩存
  • 優化入口文件大小
  • 進行關注點分離

CSS

在項目中可使用 ExtractTextWebpackPlugin 插件將 CSS 文件抽取到單獨的 bundle 中。

entry: {
    polyfill: './src/utils/polyfill.js',
    main: './src/main.js',
    app: './src/index.js'
},



複製代碼

參考連接

相關文章
相關標籤/搜索