深刻淺出的webpack構建工具---webpack3版本的CommonsChunkPlugin詳解(六)

閱讀目錄css

一:什麼是CommonsChunkPlugin, 它的做用是什麼?html

二:webpack3中CommonsChunkPlugin配置項及含義?node

一:什麼是CommonsChunkPlugin, 它的做用是什麼?jquery

     CommonsChunkPlugin插件是用來提取公共代碼的,好比一個文件它包含了多個入口chunk的公共模塊,經過將公共模塊提取出來,最終打包的文件在頁面加載的時候只加載一次(好比jquery相似的公共文件),若是該文件有改動,生成的編譯文件的hash值會改變的,所以生成的文件名就不同,那麼瀏覽器就會從新請求下該資源文件,若是該文件沒有被修改,它會在瀏覽器緩存中,之後再加載頁面時候,瀏覽器會從緩存中讀取,所以當進入該頁面的時候,只要公共文件沒有被修改,瀏覽器它都會從緩存讀取,這樣就能夠提升頁面的加載效率。固然若是咱們不使用CommonsChunkPlugin插件的話,好比第三方插件或者本身公用的模塊插件就會打包到一個文件內,好比app.js內,該app.js文件就會變大,那麼之後只要app.js文件有其餘代碼進行改動的話,那麼瀏覽器會把全部的內容都
從新加載一遍,這樣瀏覽器加載效率會很低。webpack

二:webpack3中CommonsChunkPlugin配置項及含義?ios

從官網得知,它有以下配置項:git

{ 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。github

下面咱們仍是先來看看我項目的整個目錄結構以下:web

### 目錄結構以下: 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依賴的代碼以下:npm

{ "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

查看github代碼

相關文章
相關標籤/搜索