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


閱讀目錄

一:什么是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

查看github代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM