閱讀目錄
一:什麼是CommonsChunkPlugin, 它的作用是什麼?
二:webpack3中CommonsChunkPlugin配置項及含義?
一:什麼是CommonsChunkPlugin, 它的作用是什麼?
CommonsChunkPlugin外掛是用來提取公共程式碼的,比如一個檔案它包含了多個入口chunk的公共模組,通過將公共模組提取出來,最終打包的檔案在頁面載入的時候只載入一次(比如jquery類似的公共檔案),如果該檔案有改動,生成的編譯檔案的hash值會改變的,因此生成的檔名就不一樣,那麼瀏覽器就會重新請求下該資原始檔,如果該檔案沒有被修改,它會在瀏覽器快取中,以後再載入頁面時候,瀏覽器會從快取中讀取,因此當進入該頁面的時候,只要公共檔案沒有被修改,瀏覽器它都會從快取讀取,這樣就可以提高頁面的載入效率。當然如果我們不使用CommonsChunkPlugin外掛的話,比如第三方外掛或者自己公用的模組外掛就會打包到一個檔案內,比如app.js內,該app.js檔案就會變大,那麼以後只要app.js檔案有其他程式碼進行改動的話,那麼瀏覽器會把所有的內容都
重新載入一遍,這樣瀏覽器載入效率會很低。
二:webpack3中CommonsChunkPlugin配置項及含義?
從官網得知,它有如下配置項:
{ name: string, names: string[], filename: string, minChunks: number|Infinity, chunks: string[], children: boolean, async: boolean|string, minSize: number }
我們先看下經常使用引數的具體含義如下:
name: 給這個包含公共程式碼的chunk命名
filename: 命名打包後生成的js檔案
minChunks: 公共程式碼的判斷標準,某個js模組被多少個檔案引入才算公共程式碼,預設為1,我們可以為2, 也就是說如果
一個檔案被其他頁面引入了超過了2次及以上,就可以認為該檔案就是公用程式碼。
chunks: 表示需要在哪些chunk(配置中entry的每一項)裡尋找公共程式碼進行打包,預設不設定,那麼它的尋找範圍為所有的chunk。
下面我們還是先來看看我專案的整個目錄結構如下:
### 目錄結構如下: demo1 # 工程名 | |--- dist # 打包後生成的目錄檔案 | |--- node_modules # 所有的依賴包 | |--- js # 存放所有js檔案 | | |-- demo1.js | | |-- main.js # js入口檔案 | |--- libs # 存放所有的公用外掛,庫等,比如jquery等類似的檔案 | | | |--- webpack.config.js # webpack配置檔案 | |--- index.html # html檔案 | |--- styles # 存放所有的css樣式檔案 | | |-- main.styl # main.styl檔案 | | |-- index.styl | |--- .gitignore | |--- README.md | |--- package.json | |--- .babelrc # babel轉碼檔案
首先我們先看下package.json依賴的程式碼如下:
{ "name": "demo1", "version": "1.0.0", "description": "webpack是3.0.0版本的", "main": "index.js", "scripts": { "dev": "webpack-dev-server --progress --colors --devtool cheap-module-eval-source-map --hot --inline", "build": "webpack --progress --colors --devtool cheap-module-source-map" }, "author": "", "license": "ISC", "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.7.0", "babel-preset-stage-2": "^6.24.1", "clean-webpack-plugin": "^0.1.19", "css-loader": "^1.0.0", "cssnano": "^4.0.5", "extract-text-webpack-plugin": "^3.0.2", "file-loader": "^1.1.11", "path": "^0.12.7", "postcss-cssnext": "^3.1.0", "postcss-loader": "^3.0.0", "postcss-pxtorem": "^4.0.1", "postcss-sprites": "^4.2.1", "style-loader": "^0.21.0", "stylus": "^0.54.5", "stylus-loader": "^3.0.2", "uglifyjs-webpack-plugin": "^1.2.7", "url-loader": "^1.0.1", "webpack": "^3.0.0", "webpack-cli": "^3.0.8", "webpack-dev-server": "^3.1.4" }, "dependencies": { "axios": "^0.18.0", "http-proxy-middleware": "^0.18.0", "jquery": "^3.3.1" } }
注意: 如上webpack是3.0.0版本的。然後我以為把webpack改成3.0.0以後以為就可以了,執行npm run dev 命令後,如下報錯:
然後我繼續百度搜尋 TypeError: Cannot read property 'thisCompilation' of undefined 報錯的原因,據說是extract-text-webpack-plugin版本的問題,因此我需要先解除安裝掉extract-text-webpack-plugin,然後進行重新安裝。如下命令:
安裝完成以後,我繼續執行 npm run dev 後,發現還是會報錯,報錯資訊是 TypeError: Cannot read property 'compile' of undefined,如下圖報錯所示:
同樣的道理,也一樣百度搜尋該答案,據說:webpack-dev-server如果是3.x的話,webpack必須是4.x才不會報此TypeError: Cannot read property 'compile' of undefined錯誤, 同理如果webpack是3.x,則webpack-dev-server必須是2.x
因此我們需要安裝 webpack-dev-server 2.xx版本的即可: 如下圖所示:
然後我們再進行打包就不會報錯了。因此更新後的package.json檔案如下版本:
{ "name": "demo1", "version": "1.0.0", "description": "webpack3.x.x版本的", "main": "index.js", "scripts": { "dev": "webpack-dev-server --progress --colors --devtool cheap-module-eval-source-map --hot --inline", "build": "webpack --progress --colors --devtool cheap-module-source-map" }, "author": "", "license": "ISC", "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.7.0", "babel-preset-stage-2": "^6.24.1", "clean-webpack-plugin": "^0.1.19", "css-loader": "^1.0.0", "cssnano": "^4.0.5", "extract-text-webpack-plugin": "^3.0.2", "file-loader": "^1.1.11", "path": "^0.12.7", "postcss-cssnext": "^3.1.0", "postcss-loader": "^3.0.0", "postcss-pxtorem": "^4.0.1", "postcss-sprites": "^4.2.1", "style-loader": "^0.21.0", "stylus": "^0.54.5", "stylus-loader": "^3.0.2", "uglifyjs-webpack-plugin": "^1.2.7", "url-loader": "^1.0.1", "webpack": "^3.0.0", "webpack-cli": "^3.0.8", "webpack-dev-server": "^2.3.0" }, "dependencies": { "axios": "^0.18.0", "http-proxy-middleware": "^0.18.0", "jquery": "^3.3.1" } }
下面我們繼續看下我們的入口檔案 main.js 程式碼如下:
require('jquery');
webpack.config.js 配置程式碼如下:
const path = require('path'); // 提取css的外掛 const ExtractTextPlugin = require('extract-text-webpack-plugin'); const ClearWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: './js/main.js', output: { filename: 'bundle.js', // 將輸出的檔案都放在dist目錄下 path: path.resolve(__dirname, 'dist'), publicPath: '/dist' }, mode: 'development', module: { rules: [ { // 使用正則去匹配 test: /\.styl$/, use: ExtractTextPlugin.extract({ fallback: { loader: 'style-loader' }, use: [ { loader: 'css-loader', options: {} }, { loader: 'postcss-loader', options: { ident: 'postcss', plugins: [ require('postcss-cssnext')(), require('cssnano')(), require('postcss-pxtorem')({ rootValue: 100, unitPrecision: 5, propWhiteList: [] }), require('postcss-sprites')() ] } }, { loader: 'stylus-loader', options: {} } ] }) }, { test: /\.(png|jpg)$/, loader: 'url-loader', options: { limit: 10000, name: '[name].[ext]' } }, { test: /\.js$/, exclude: /(node_modules)/, // 排除檔案 loader: 'babel-loader' } ] }, resolve: { // modules: ['plugin', 'js'] }, /* externals: { jquery: 'jQuery' }, */ devtool: 'source-map', devServer: { // contentBase: path.join(__dirname, "dist"), port: 8081, host: '0.0.0.0', headers: { 'X-foo': '112233' }, // hot: true, inline: true, // open: true, overlay: true, stats: 'errors-only' }, plugins: [ new ExtractTextPlugin({ // 從js檔案中提取出來的 .css檔案的名稱 filename: `main.css` }) ] };
如上是所有的配置,配置程式碼的含義是把入口檔案的main.js 程式碼打包到dist目錄下的 bundle.js 程式碼內,那麼由於main.js 程式碼內把jquery.js引入進來了,
因此他會把jquery也會打包到main.js程式碼內部去;
我們繼續檢視dist目錄下的bundle.js,自己可以檢視程式碼可以看到會把jquery所有程式碼打包進去了。當然在webpack中,我們可以把 externals配置項開啟,加上如下程式碼,也不會把jquery打包到bundle.js內部,bundle.js內部會對jquery做了一個引用而已:如下程式碼:
externals: { jquery: 'jQuery' },
但是在頁面上需要引用jquery原始檔程式碼,如index.html上的程式碼引入了
<script src="http://code.jquery.com/jquery-1.12.0.min.js"></script>
比如index.html 程式碼如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="http://code.jquery.com/jquery-1.12.0.min.js"></script> <link href="dist/main.css" rel="stylesheet" type="text/css" /> </head> <body> <div id="app"></div> <script src="dist/bundle.js"></script> </body> </html>
關於 externals的理解,可以看這篇文章(https://www.cnblogs.com/tugenhua0707/p/9384953.html#_labe2_8)
上面只是講解了下 關於不使用 CommonsChunkPlugin 外掛,它會把require依賴的公用檔案會打包到bundle.js程式碼內。
下面來講解下使用 CommonsChunkPlugin 外掛如何來避免這種情況的發生。
1. 首先我們先要引入CommonsChunkPlugin 如下程式碼:
// 引入 CommonsChunkPlugin 外掛 const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
2. 在plugins裡面配置下即可,如下程式碼:
module.exports = { plugins: [ new CommonsChunkPlugin({ name: 'vender', // 公共程式碼的chunk命名為 'verder' filename: '[name].bundle.js' // 生成的檔名為 vender.bundle.js }) ] };
3. 入口檔案改成如下:
entry: { vender: ['./libs/jquery.js'], main: './js/main.js' }
如上它會把jquery等類似的檔案打包到 vender.js中了。vender它是一個陣列,裡面可以存放多個類似的檔案,都可以打包到一個公用的檔案裡面去,
但是也要考慮好,檔案大小,如果10幾個庫檔案打包到一個vender.js內的話,那麼該檔案將會變得很大,那麼頁面在載入的時候,會影響頁面載入的效率,
所以在這種情況下,可以分多個檔案分別打包到檔案內,具體的看自己在專案中權衡吧。
3.1 為了打包html檔案,我們引入了 html-webpack-plugin 外掛,如下需要引入的程式碼:
const HtmlWebpackPlugin = require('html-webpack-plugin');
3.2 打包之前我們需要清除dist目錄下的檔案,然後再生成新的包,因此我們引入 clean-webpack-plugin 外掛,如下程式碼:
// 清除dist目錄下的檔案 const ClearWebpackPlugin = require('clean-webpack-plugin');
因此plugins的所有外掛配置如下:
module.exports = { plugins: [ new ClearWebpackPlugin(['dist']), new HtmlWebpackPlugin({ template: './index.html' // 模版檔案 }), new ExtractTextPlugin({ // 從js檔案中提取出來的 .css檔案的名稱 filename: `main.css` }), new CommonsChunkPlugin({ name: 'vender', // 公共程式碼的chunk命名為 'verder' filename: '[name].bundle.js' // 生成的檔名為 vender.bundle.js }) ] }
所以在專案的根目錄下,我們index.html程式碼變成如下了:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app">22222</div> <div class="test1">12aaa</div> <div class='test2'>vvvvv</div> </body> </html>
js/main.js 程式碼如下:
require('../styles/main.styl');
$('#app').html('歡迎你來我的部落格'); // 就可以使用了
webpack.config.js 所有配置程式碼如下:
const path = require('path'); // 提取css的外掛 const ExtractTextPlugin = require('extract-text-webpack-plugin'); // 清除dist目錄下的檔案 const ClearWebpackPlugin = require('clean-webpack-plugin'); const webpack = require('webpack'); // 引入打包html檔案 const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入 CommonsChunkPlugin 外掛 const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin'); module.exports = { entry: { vender: ['./libs/jquery.js'], main: './js/main.js' }, output: { filename: '[name].js', // 將輸出的檔案都放在dist目錄下 path: path.resolve(__dirname, 'dist'), publicPath: './' }, module: { rules: [ { // 使用正則去匹配 test: /\.styl$/, use: ExtractTextPlugin.extract({ fallback: { loader: 'style-loader' }, use: [ { loader: 'css-loader', options: {} }, { loader: 'postcss-loader', options: { ident: 'postcss', plugins: [ require('postcss-cssnext')(), require('cssnano')(), require('postcss-pxtorem')({ rootValue: 16, unitPrecision: 5, propWhiteList: [] }), require('postcss-sprites')() ] } }, { loader: 'stylus-loader', options: {} } ] }) }, { test: /\.(png|jpg)$/, loader: 'url-loader', options: { limit: 10000, name: '[name].[ext]' } }, { test: /\.js$/, exclude: /(node_modules)/, // 排除檔案 loader: 'babel-loader' } ] }, resolve: { // modules: ['plugin', 'js'] }, devtool: 'source-map', devServer: { // contentBase: path.join(__dirname, "dist"), port: 8081, host: '0.0.0.0', headers: { 'X-foo': '112233' }, // hot: true, inline: true, // open: true, overlay: true, stats: 'errors-only' }, plugins: [ new ClearWebpackPlugin(['dist']), new HtmlWebpackPlugin({ template: './index.html' // 模版檔案 }), new ExtractTextPlugin({ // 從js檔案中提取出來的 .css檔案的名稱 filename: `main.css` }), new CommonsChunkPlugin({ name: 'vender', // 公共程式碼的chunk命名為 'verder' filename: '[name].bundle.js' // 生成的檔名為 vender.bundle.js }) ] };
3.3. 模組重複引用的問題,最終會把相同的模組打包到入口檔案內。
現在我們js目錄下有如下檔案:
js/main.js 入口檔案,程式碼如下:
require('../styles/main.styl'); $('#app').html('歡迎你來我的部落格'); require('./demo1.js'); require('./main2.js');
js/main2.js 程式碼如下:
require('./demo1.js');
js/demo1.js 程式碼如下:
export default function printMe() { console.log('11111111'); }
打包後,發現main.js原始碼內只有一份程式碼:如下main.js程式碼:
webpackJsonp([0],{ /***/ 36: /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony export (immutable) */ __webpack_exports__["default"] = printMe; function printMe() { console.log('11111111'); } console.log(a); /***/ }), /***/ 72: /***/ (function(module, exports, __webpack_require__) { __webpack_require__(73); $('#app').html('歡迎你來我的部落格'); __webpack_require__(36); __webpack_require__(74); /***/ }), /***/ 73: /***/ (function(module, exports) { // removed by extract-text-webpack-plugin /***/ }), /***/ 74: /***/ (function(module, exports, __webpack_require__) { __webpack_require__(36); /***/ }) },[72]); //# sourceMappingURL=main.js.map
3.4 多入口,模組重複引用,會將多個引用模組被打包到公共模組。
webpack中enter配置如下:
entry: { vender: ['./libs/jquery.js'], main: './js/main.js', main2: './js/main2.js' },
比如main.js 程式碼如下:
require('../styles/main.styl'); $('#app').html('歡迎你來我的部落格'); console.log('這是main.js'); require('./demo1.js');
main2.js 程式碼如下:
console.log('這是main2.js');
require('./demo1.js');
如上main.js和main2.js 程式碼都引用了 demo1.js,demo1.js 程式碼如下:
export default function printMe() { console.log('11111111'); }
然後通過如上程式碼後,main1.js程式碼打包後變為如下:
webpackJsonp([0],{ /***/ 72: /***/ (function(module, exports, __webpack_require__) { __webpack_require__(73); $('#app').html('歡迎你來我的部落格'); console.log('這是main.js'); __webpack_require__(36); /***/ }), /***/ 73: /***/ (function(module, exports) { // removed by extract-text-webpack-plugin /***/ }) },[72]); //# sourceMappingURL=main.js.map
main2.js 程式碼如下:
webpackJsonp([1],{ /***/ 74: /***/ (function(module, exports, __webpack_require__) { console.log('這是main2.js'); __webpack_require__(36); /***/ }) },[74]); //# sourceMappingURL=main2.js.map
可以看到公用程式碼被提取到vender.js程式碼內部了。
所有的webpack.config.js程式碼如下:
const path = require('path'); // 提取css的外掛 const ExtractTextPlugin = require('extract-text-webpack-plugin'); // 清除dist目錄下的檔案 const ClearWebpackPlugin = require('clean-webpack-plugin'); const webpack = require('webpack'); // 引入打包html檔案 const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入 CommonsChunkPlugin 外掛 const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin'); module.exports = { entry: { vender: ['./libs/jquery.js'], main: './js/main.js', main2: './js/main2.js' }, output: { filename: '[name].js', // 將輸出的檔案都放在dist目錄下 path: path.resolve(__dirname, 'dist'), publicPath: './' }, module: { rules: [ { // 使用正則去匹配 test: /\.styl$/, use: ExtractTextPlugin.extract({ fallback: { loader: 'style-loader' }, use: [ { loader: 'css-loader', options: {} }, { loader: 'postcss-loader', options: { ident: 'postcss', plugins: [ require('postcss-cssnext')(), require('cssnano')(), require('postcss-pxtorem')({ rootValue: 16, unitPrecision: 5, propWhiteList: [] }), require('postcss-sprites')() ] } }, { loader: 'stylus-loader', options: {} } ] }) }, { test: /\.(png|jpg)$/, loader: 'url-loader', options: { limit: 10000, name: '[name].[ext]' } }, { test: /\.js$/, exclude: /(node_modules)/, // 排除檔案 loader: 'babel-loader' } ] }, resolve: { extensions: ['*', '.js', '.json'] }, devtool: 'source-map', devServer: { // contentBase: path.join(__dirname, "dist"), port: 8081, host: '0.0.0.0', headers: { 'X-foo': '112233' }, // hot: true, inline: true, // open: true, overlay: true, stats: 'errors-only' }, plugins: [ new ClearWebpackPlugin(['dist']), new HtmlWebpackPlugin({ template: './index.html' // 模版檔案 }), new ExtractTextPlugin({ // 從js檔案中提取出來的 .css檔案的名稱 filename: `main.css` }), new CommonsChunkPlugin({ name: 'vender', // 公共程式碼的chunk命名為 'verder' filename: '[name].bundle.js' // 生成的檔名為 vender.bundle.js }) ] };
3.4 將第三方業務模組分開打包。
如上我們把公用程式碼和jquery原始碼都打包到了vender.js內部去了,但是有時候我們想jquery型別第三方業務程式碼單獨在一個檔案內,公用的程式碼打包到另一個檔案內,這樣除錯程式碼也比較方便看程式碼,因此我們可以如下配置:
CommonsChunkPlugin 配置改為如下:
new CommonsChunkPlugin({ name: ['chunk', 'vender'], filename: '[name].bundle.js' // 生成的檔名為 vender.bundle.js });
如上它會把 jquery等類庫的程式碼打包到vender.js內部,但是會把頁面上其他的公用程式碼打包到 chunk.js 程式碼內部,如下dist目錄
chunk.bundle.js程式碼如下:
webpackJsonp([2],{ /***/ 36: /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony export (immutable) */ __webpack_exports__["default"] = printMe; function printMe() { console.log('11111111'); } /***/ }) }); //# sourceMappingURL=chunk.bundle.js.map