知多一點 webpack 的 CommonsChunkPlugin

hello~親愛的看官老爺們你們好~ 最近一直在學習 webpack 的相關知識。曾幾什麼時候我總以爲 webpack 的體系龐大而難以掌握,一直迴避不肯去學。然而偉人魯迅曾說過: 世上太多事會因沒法掌握而使你狂躁不安,最好的解決方法就是硬着頭皮開始作! 於是就從比較簡單的 CommonsChunkPlugin 開始學起吧~ css

雖然本文比較簡單,但仍是須要一點 webpack 知識的,如若徹底沒接觸過 webpack ,建議先移步 官方文檔Webpack 3,從入門到放棄 瞭解一下 webpack 基礎爲佳~html

基礎配置

CommonsChunkPlugin 插件,是一個可選的用於創建一個獨立文件(又稱做 chunk)的功能,這個文件包括多個入口 chunk 的公共模塊。經過將公共模塊拆出來,最終合成的文件可以在最開始的時候加載一次,便存起來到緩存中供後續使用。這個帶來速度上的提高,由於瀏覽器會迅速將公共的代碼從緩存中取出來,而不是每次訪問一個新頁面時,再去加載一個更大的文件。vue

簡單來講,這有點像封裝函數。把不變的與變化的分開,使得不變的能夠高效複用,變化的靈活配置。接下來會根據這個原則優化咱們的項目,如今先看看虛擬的項目長成什麼樣吧~node

新建一個 index.html 模板與入口 index.js文件,簡單配置以下:jquery

index.htmlwebpack

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <p>{{ vue_test }}</p>
    </div>
    <div class="jq_test"></div>
</body>
</html>複製代碼

index.js:git

import Vue from 'vue';
import $ from 'jquery';

new Vue({
  el: '#app',
  data: {
    vue_test: 'vue is loaded!'
  }
})

$(function() {
  $('.jq_test').html('jquery is loaded!')
})複製代碼

爲演示起見,代碼十分簡單,相信不用多加解釋。接下來先簡單配置一下 webpack.config.js,代碼以下:github

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  entry: {
    index: path.join(__dirname, 'index.js')
  },
  output: {
    path: path.join(__dirname, '/dist'),
    filename: 'js/[name].[chunkhash].js'
  },
  resolve: { alias: { 'vue': 'vue/dist/vue.js' } },
  plugins: [
    new CleanWebpackPlugin(['./dist']),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: true
    }),
    new BundleAnalyzerPlugin(),
  ]
};複製代碼

CleanWebpackPlugin 主要用於清除 dist 目錄下的文件,這樣每次打包就沒必要手動清除了。HtmlWebpackPlugin 則是爲了在 dist 目錄下新建 html 模板並自動插入依賴的 jsBundleAnalyzerPlugin 主要是爲了生成打包後的 js 文件包含的依賴,如此時進行打包,則生成:web

能夠看到生成的 index.js 文件包含了 vuejqueryvuex

首次優化

通常而言,咱們項目中的類庫變化較少,業務代碼卻是多變的。須要想辦法把類庫抽離出來,把業務代碼單獨打包。這樣加傷 hash 後瀏覽器就能緩存類庫的 js 文件,優化用戶體驗。此時咱們的主角 CommonsChunkPlugin 就正式登場了。咱們在 webpack.config.js 文件的 plugins 中添加 CommonsChunkPlugin,配置以下:

plugins: [
    //...此前的代碼
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: function(module) {
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, './node_modules')
          ) === 0
        )
      }
    }),
]複製代碼

上述配置,是經過 CommonsChunkPlugin 生成一個名爲 vendorjs 文件,它抽取入口文件也就是 index.js 中來源於 node_modules 的依賴組成。此例中就是 vuejquery。打包出來畫風是這樣的:

此時看上去解決了咱們的問題,將依賴的類庫抽取抽來獨立打包,加上緩存就能被瀏覽器緩存了。然而事情沒那麼簡單,不行你隨意改一下入口的 index.js 代碼,再次打包:

絕望地發現 vendor.js 文件的 hash 改變了。簡單說,這是由於模塊標識產生了變化所致使的,更具體的緣由能夠查看相關的中文文檔~修正的方法其實也挺簡單,就是再使用 CommonsChunkPlugin 抽取一次模塊,將不變的類庫沉澱下來,將變化的抽離出去。於是添以下代碼:

plugins: [
    //...此前的代碼
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: function(module) {
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, './node_modules')
          ) === 0
        )
      }
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      chunks: ['vendor', 'index']
    })
]複製代碼

打包後, dist/js 目錄下多出一個名爲 manifestjs 文件,此時你不管如何改變 index.js 的代碼,打包後的 vendor.jshash 都再也不會改變了。

然而稍等,當你想拍拍手收工的時候,思考一下這樣的場景:隨着項目不斷迭代,vendor 中的依賴不斷被添加與刪除,使得它的 hash 會不斷變化,這顯然不符合咱們的利益,這到底如何解決呢?

再次優化

既然 CommonsChunkPlugin 是能夠按照咱們的需求抽取模塊,而依賴的外部模塊多是不斷變化的,那麼爲什麼不將基礎的依賴模塊抽取出來做爲一個文件,其餘的依賴如插件等做爲另外一個文件呢?

簡單說,如咱們的項目中 vue 是基本的依賴,必須用到它,而 jquery 等則是後加的類庫,以後可能變動。那麼將 vue 獨立打包一個文件,有利於瀏覽器緩存,由於不管此後添加更多的類庫或刪去 jquery 時, vue 文件的緩存依然是生效的。於是咱們能夠這麼作,首先新建一個入口:

entry: {
    index: path.join(__dirname, 'index.js'),
    vendor: ['vue'],
},複製代碼

此處主要是用於指明須要獨立打包的依賴有哪些。以後在 plugins 中作以下修改:

plugins: [
    //...此前的代碼
    new webpack.HashedModuleIdsPlugin(),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: Infinity,
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'common',
      minChunks: function(module) {
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, './node_modules')
          ) === 0
        )
      },
      chunks: ['index'],
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      chunks: ['vendor', 'common', 'index']
    })
]複製代碼

插件 HashedModuleIdsPlugin,是用於保持模塊引用的 module id 不變。而 CommonsChunkPlugin 則提取入口指定的依賴獨立打包,minChunks: Infinity,的用意是讓插件別管其餘,就按照設置的數組提取文件就好。以後修改一下原來的 vendor,重命名爲 common,指定它從入口 index.js 中抽取來自 node_modules 的依賴。最後就是抽取 webpack 運行時的函數及其模塊標識組成 manifest。運行一下 webpack,構建出來如圖:

能夠看到 vuejquery 被分開打包成了兩個文件,咱們嘗試添加一下新的依賴 vuex,打包後結果以下:

如此一來,咱們的優化目的就達到了,不變的都提取出來,變化的能夠動態配置~

小結

webpack 插件 CommonsChunkPlugin 就介紹到這裏了,然而優化仍是有不少的,好比開啓壓縮,去除註釋等。而當項目體積逐漸增大時,CommonsChunkPlugin 就不必定是提取代碼的最優解了。在打包速度與控制構建的精細程度來講,結合 DLLPlugin 會有更好的表現。根據不一樣的場景組合不一樣的插件以達到咱們的目的,原本就是 webpack 的魅力之一。

感謝各位看官大人看到這裏,知易行難,但願本文對你有所幫助,全部的代碼均會被上傳到 github 上,滾求 star ~謝謝!

相關文章
相關標籤/搜索