• 深入浅出的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代码

  • 相关阅读:
    李彦宏:创业成功五招即可
    JS无聊之作——换肤切换样式
    从3个科技公司里学到的57条经验(转载)
    早该知道的7个JavaScript技巧
    ASP.NET Cookie 概述
    曝光SEO高手藏在内心的SEO秘籍
    18种最实用的网站推广方法大全
    javascript的IE和Firefox兼容性问题
    增加反向链接的35个技巧
    常用JS片段
  • 原文地址:https://www.cnblogs.com/tugenhua0707/p/9500794.html
Copyright © 2020-2023  润新知