hello~親愛的看官老爺們大家好~ 最近一直在學習 webpack
的相關知識。曾幾何時我總覺得 webpack
的體系龐大而難以掌握,一直迴避不願去學。然而偉人魯迅曾說過: 世上太多事會因無法掌握而使你狂躁不安,最好的解決方法就是硬著頭皮開始做! 因而就從比較簡單的 CommonsChunkPlugin
開始學起吧~
雖然本文比較簡單,但還是需要一點 webpack
知識的,如若完全沒接觸過 webpack
,建議先移步 官方文件 與 Webpack 3,從入門到放棄 瞭解一下 webpack
基礎為佳~
基礎配置
CommonsChunkPlugin
外掛,是一個可選的用於建立一個獨立檔案(又稱作 chunk)的功能,這個檔案包括多個入口 chunk 的公共模組。通過將公共模組拆出來,最終合成的檔案能夠在最開始的時候載入一次,便存起來到快取中供後續使用。這個帶來速度上的提升,因為瀏覽器會迅速將公共的程式碼從快取中取出來,而不是每次訪問一個新頁面時,再去載入一個更大的檔案。
簡單來說,這有點像封裝函式。把不變的與變化的分開,使得不變的可以高效複用,變化的靈活配置。接下來會根據這個原則優化我們的專案,現在先看看虛擬的專案長成什麼樣吧~
新建一個 index.html
模板與入口 index.js
檔案,簡單配置如下:
index.html
:
<!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
:
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
,程式碼如下:
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
模板並自動插入依賴的 js
。 BundleAnalyzerPlugin
主要是為了生成打包後的 js
檔案包含的依賴,如此時進行打包,則生成:
可以看到生成的 index.js
檔案包含了 vue
與 jquery
。
首次優化
一般而言,我們專案中的類庫變化較少,業務程式碼倒是多變的。需要想辦法把類庫抽離出來,把業務程式碼單獨打包。這樣加傷 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
生成一個名為 vendor
的 js
檔案,它抽取入口檔案也就是 index.js
中來源於 node_modules
的依賴組成。此例中就是 vue
與 jquery
。打包出來畫風是這樣的:
此時看上去解決了我們的問題,將依賴的類庫抽取抽來獨立打包,加上快取就能被瀏覽器快取了。然而事情沒那麼簡單,不行你隨意改一下入口的 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
目錄下多出一個名為 manifest
的 js
檔案,此時你無論如何改變 index.js
的程式碼,打包後的 vendor.js
的 hash
都不再會改變了。
然而稍等,當你想拍拍手收工的時候,思考一下這樣的場景:隨著專案不斷迭代,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
,構建出來如圖:
可以看到 vue
與 jquery
被分開打包成了兩個檔案,我們嘗試新增一下新的依賴 vuex
,打包後結果如下:
如此一來,我們的優化目的就達到了,不變的都提取出來,變化的可以動態配置~
小結
webpack
外掛 CommonsChunkPlugin
就介紹到這裡了,然而優化還是有很多的,比如開啟壓縮,去除註釋等。而當專案體積逐漸增大時,CommonsChunkPlugin
就不一定是提取程式碼的最優解了。在打包速度與控制構建的精細程度來說,結合 DLLPlugin
會有更好的表現。根據不同的場景組合不同的外掛以達到我們的目的,本來就是 webpack
的魅力之一。
感謝各位看官大人看到這裡,知易行難,希望本文對你有所幫助,所有的程式碼均會被上傳到 github 上,滾求 star
~謝謝!