webpack

94 阅读10分钟

第 0 章:写在zui前面

​ 在VsCode里面安装jquery,再在js文件里 import $ from 'jquery';

​ 引入less文件时,需要工具编译为css。还有很多需要工具编译,因为浏览器无法读取。此时则需要一个构建工具——webpack。

1 章:webpack 简介

1.1 webpack 是什么

​ webpack 是一种前端资源构建工具,一个静态模块打包器(module bundler)。在 webpack 看来, 前端的所有资源文件(js/json/css/img/less/...)都会作为模块处理。它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)。

1.2 webpack 五个核心概念

1.2.1 Entry

入口(Entry)指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。

1.2.2 Output

输出(Output)指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。

1.2.3 Loader

Loader 让 webpack 能 够 去 处 理 那 些 非 JavaScript 文 件 (webpack 自 身 只 理 解 JavaScript)

1.2.4 Plugins

插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩, 一直到重新定义环境中的变量等。

1.2.5 Mode

模式(Mode)指示 webpack 使用相应模式的配置。

2 章:webpack的初体验

2.1 初始化配置

  1. 初始化 package.json

​ 输入指令:

npm init

  1. 下载并安装 webpack

​ 输入指令:

npm install webpack webpack-cli -g

npm install webpack webpack-cli -D

2.2 编译打包应用

​ html引入的是打包后资源

  1. 创建文件

  2. 运行指令

​ 开发环境指令:webpack src/js/index.js -o build/js/built.js --mode=development

webpack会以 ./src/index.js 为入口文件开始打包,打包后输出到 ./build/built.js(整体打包环境,是开发环境)				

​ 功能:webpack 能够编译打包 js 和 json 文件,并且能将 es6 的模块化语法转换成 浏览器能识别的语法。

​ 生产环境指令:webpack src/js/index.js -o build/js/built.js --mode=production

​ 功能:在开发配置功能上多一个功能,压缩代码。

  1. 结论

​ (1)webpack 能够编译打包 js 和 json 文件。

​ (2)能将 es6 的模块化语法转换成浏览器能识别的语法。

​ (3)能压缩代码。

  1. 问题

​ (1)不能编译打包 css、img 等文件。

​ (2)不能将 js 的 es6 基本语法转化为 es5 以下语法。

3 **章:**webpack 开发环境的基本配置

3.1 创建配置文件

  1. 创建文件 webpack.config.js

    作用: 指示 webpack 干哪些活(当你运行 webpack 指令时,会加载里面的配置),所有构建工具都是基于nodejs平台运行的~模块化默认采用commonjs。

  2. 配置内容如下

    // resolve用来拼接绝对路径的方法
    const { resolve } = require('path'); 			// node 内置核心模块,用来处理路径问题。
    module.exports = { 
        entry: './src/js/index.js', 				// 入口文件 
        output: { 									// 输出配置 
            filename: './built.js', 				// 输出文件名
            path: resolve(__dirname, 'build/js') 	// 输出文件路径配置
        },
        mode: 'development' 						//开发环境 
    };
    
  3. 运行指令: webpack

  4. 结论: 此时功能与上节一致

3.2 打包样式资源

  1. 创建文件

    //index.js
    // 引入样式资源
    import './index.css';
    import './index.less';
    
  2. 下载安装 loader 包

    npm i css-loader style-loader less-loader less -D

  3. 修改配置文件

    // resolve 用来拼接绝对路径的方法 
    const { resolve } = require('path'); 
    
    module.exports = { 
        // webpack 配置 
        // 入口起点 
        entry: './src/index.js', 
        // 输出 
        output: { 
            // 输出文件名 
            filename: 'built.js', 
            // 输出路径 
            // __dirname nodejs 的变量,代表当前文件的目录绝对路径 
            path: resolve(__dirname, 'build')
        },
        // loader 的配置 
        module: { 
            rules: [ 
                // 详细 loader 配置 
                // 不同文件必须配置不同 loader 处理 
                { 
                    // 匹配哪些文件 
                    test: /\.css$/, 
                    // 使用哪些 loader 进行处理 
                    use: [ 
                        // use 数组中 loader 执行顺序:从右到左,从下到上 依次执行 
                        // 创建 style 标签,将 js 中的样式资源插入进行,添加到 head 中生效 
                        'style-loader', 
                        // 将 css 文件变成 commonjs 模块加载 js 中,里面内容是样式字符串 
                        'css-loader' ] 
                },
                { 
                    test: /\.less$/, 
                    use: [ 
                        'style-loader',
                        'css-loader', 
                        // 将 less 文件编译成 css 文件
                        // 需要下载 less-loader 和 less
                        'less-loader' 
                    ] 
                } 
            ] 
        },
        // plugins 的配置 
        plugins: [ 
            // 详细 plugins 的配置 
        ],
        // 模式 
        mode: 'development',// 开发模式 
        //生产模式会压缩
        // mode: 'production' 
    }
    
  4. 运行指令: webpack

3.3 打包 HTML 资源

  1. 创建文件

  2. 下载安装 plugin 包

npm install --save-dev html-webpack-plugin

  1. 修改配置文件

    /*
      loader: 1. 下载   2. 使用(配置loader)
      plugins: 1. 下载  2. 引入  3. 使用
    */
    const { resolve } = require('path'); 
    const HtmlWebpackPlugin = require('html-webpack-plugin'); 
    
    module.exports = { 
        entry: './src/index.js', 
        output: { 
            filename: 'built.js', 
            path: resolve(__dirname, 'build') 
        },
        module: { 
            rules: [ 
                // loader 的配置 
            ] 
        },
        plugins: [ 
            // plugins 的配置 
            // html-webpack-plugin 
            // 功能:默认会创建一个空的 HTML,自动引入打包输出的所有资源(JS/CSS) 
            // 需求:需要有结构的 HTML 文件 
            new HtmlWebpackPlugin({ 
                // 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS) 
                template: './src/index.html' 
            }) 
        ],
        mode: 'development' 
    };
    
  2. 运行指令: webpack

3.4 打包图片资源

  1. 创建文件

  2. 下载安装 loader 包

npm install --save-dev html-loader url-loader file-loader

  1. 修改配置文件

    const { resolve } = require('path'); 
    const HtmlWebpackPlugin = require('html-webpack-plugin'); 
    
    module.exports = { 
        entry: './src/index.js', 
        output: { 
            filename: 'built.js', 
            path: resolve(__dirname, 'build') 
        },
        module: { 
            rules:[
                { 
                    test: /\.less$/, 
                    // 要使用多个 loader 处理用 use 
                    use: ['style-loader', 'css-loader', 'less-loader'] 
                },
                { 
                    // 问题:默认处理不了 html 中 img 图片 
                    // 处理图片资源 
                    test: /\.(jpg|png|gif)$/, 
                    // 使用一个 loader 
                    // 下载 url-loader file-loader 
                    loader: 'url-loader', 
                    options: {
                        // 图片大小小于 8kb,就会被 base64 处理 
                        // 优点: 减少请求数量(减轻服务器压力) 
                        // 缺点:图片体积会更大(文件请求速度更慢) 
                        limit: 8 * 1024, 
                        // 问题:因为 url-loader 默认使用 es6 模块化解析,而 html-loader 引入图片是 commonjs 
                        // 解析时会出问题:[object Module] 
                        // 解决:关闭 url-loader 的 es6 模块化,使用 commonjs 解析 
                        esModule: false, 
                        // 给图片进行重命名 
                        // [hash:10]取图片的 hash 的前 10 位 
                        // [ext]取文件原来扩展名 
                        name: '[hash:10].[ext]' 
                    } 
                },
                { 
                    test: /\.html$/, 
                    // 处理 html 文件的 img 图片(负责引入 img,从而能被 url-loader 进行处理) 
                    loader: 'html-loader' 
                } 
            ] 
        },
        plugins: [ 
            new HtmlWebpackPlugin({ template: './src/index.html' }) 
        ],
        mode: 'development' 
    };
    
  2. 运行指令: webpack

3.5 打包其他资源

  1. 创建文件

  2. 修改配置文件

    const { resolve } = require('path'); 
    const HtmlWebpackPlugin = require('html-webpack-plugin'); 
    
    module.exports = { 
        entry: './src/index.js', 
        output: { 
            filename: 'built.js', 
            path: resolve(__dirname, 'build') 
        },
        module: { 
            rules: [ 
                { 
                    test: /\.css$/, 
                    use: ['style-loader', 'css-loader'] 
                },
                // 打包其他资源(除了 html/js/css 资源以外的资源) 
                { 
                    // 排除 css/js/html 资源 
                    exclude: /\.(css|js|html|less)$/, 
                    loader: 'file-loader', 
                    options: { 
                        name: '[hash:10].[ext]' 
                    } 
                } 
            ] 
        },
        plugins: [ 
            new HtmlWebpackPlugin({ template: './src/index.html' })
        ],
        mode: 'development' 
    };
    
  3. 运行指令: webpack

3.6 devserver

​ 开发服务器:自动打包,只用改源代码

  1. 创建文件

  2. 修改配置文件

    const { resolve } = require('path'); 
    const HtmlWebpackPlugin = require('html-webpack-plugin'); 
    
    module.exports = { 
        entry: './src/index.js', 
        output: { 
            filename: 'built.js', 
            path: resolve(__dirname, 'build') 
        },
        module: { 
            rules: [ 
                { 
                    test: /\.css$/, 
                    use: ['style-loader', 'css-loader'] 
                },
                // 打包其他资源(除了 html/js/css 资源以外的资源) 
                { 
                    // 排除 css/js/html 资源 
                    exclude: /\.(css|js|html|less)$/,
                    loader: 'file-loader', 
                    options: { name: '[hash:10].[ext]' } 
                } 
            ] 
        },
        plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }) ],
        mode: 'development',
        // 开发服务器 devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~~)
      	// 特点:只会在内存中编译打包,不会有任何输出
        devServer: { 
            // 项目构建后路径 
            contentBase: resolve(__dirname, 'build'), 
            // 启动 gzip 压缩 
            compress: true, 
            // 端口号 
            port: 3000, 
            // 自动打开浏览器 
            open: true 
        } 
    };
    
  3. 运行指令: npx webpack-dev-server

3.7 开发环境配置(总)

  1. 创建文件

  2. 修改配置文件

    const { resolve } = require('path'); 
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = { 
        entry: './src/js/index.js', 
        output: { 
            filename: 'js/built.js', 
            path: resolve(__dirname, 'build') 
        },
        module: { 
            rules: [ 
                // loader 的配置 
                { 
                    // 处理 less 资源 
                    test: /\.less$/, 
                    use: ['style-loader', 'css-loader', 'less-loader'] 
                },
                { 
                    // 处理 css 资源 
                    test: /\.css$/, 
                    use: ['style-loader', 'css-loader'] 
                },
                {
                    // 处理图片资源 
                    test: /\.(jpg|png|gif)$/, 
                    loader: 'url-loader', 
                    options: 
                    { 
                        limit: 8 * 1024, 
                        name: '[hash:10].[ext]', 
                        // 关闭 es6 模块化 
                        esModule: false, 
                        outputPath: 'imgs' 
                    } 
                },
                { 
                    // 处理 html 中 img 资源 
                    test: /\.html$/, 
                    loader: 'html-loader' 
                },
                { 
                    // 处理其他资源 
                    exclude: /\.(html|js|css|less|jpg|png|gif)/, 
                    loader: 'file-loader', 
                    options: 
                    { 
                        name: '[hash:10].[ext]', 
                        outputPath: 'media' 
                    } 
                } 
            ] 
        },
        plugins: [ 
            // plugins 的配置 
            new HtmlWebpackPlugin({ template: './src/index.html' }) 
        ],
        mode: 'development', 
        devServer: { 
            contentBase: resolve(__dirname, 'build'), 
            compress: true, 
            port: 3000, 
            open: true 
        } 
    };
    
  3. 运行指令: npx webpack-dev-server

4 **章:**webpack 生产环境的基本配置

4.0 最前面

兼容性、压缩等问题放在生产环境做,这样开发环境打包的时间就不会被拖慢,速率增加。

4.1 提取 css 成单独文件

  1. 下载安装包

  2. 下载插件

    npm install --save-dev mini-css-extract-plugin

  3. 修改配置文件

    const { resolve } = require('path'); 
    const HtmlWebpackPlugin = require('html-webpack-plugin'); 
    
    const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 
    module.exports = { 
        entry: './src/js/index.js',
        output: { 
            filename: 'js/built.js',
            path: resolve(__dirname, 'build')
        },
        module: {
            rules: [
                {
                    test: /\.css$/,
                    use: [
                        // 创建 style 标签,将样式放入
                        // 'style-loader',
                        // 这个 loader 取代 style-loader。作用:提取 js 中的 css 成单独文件
                        MiniCssExtractPlugin.loader,
                        // 将 css 文件整合到 js 文件中
                        'css-loader'
                    ]
                }
            ]
        },
        plugins: [
            new HtmlWebpackPlugin({
                template: './src/index.html'
            }),
            new MiniCssExtractPlugin({
                // 对输出的 css 文件进行重命名
                filename: 'css/built.css'
            })
        ],
        mode: 'development'
    };
    
  4. 运行指令: webpack

4.2 css 兼容性处理

  1. 创建文件

  2. 下载 loader

    npm install --save-dev postcss-loader postcss-preset-env

  3. 修改配置文件

    const { resolve } = require('path'); 
    const HtmlWebpackPlugin = require('html-webpack-plugin'); 
    const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 
    
    // 设置 nodejs 环境变量 
    // process.env.NODE_ENV = 'development'; 
    
    module.exports = { 
        entry: './src/js/index.js', 
        output: { 
            filename: 'js/built.js', 
            path: resolve(__dirname, 'build') 
        },
        module: { 
            rules: [ 
                {
                    test: /\.css$/, 
                    use: [ 
                        MiniCssExtractPlugin.loader, 
                        'css-loader', 
        /*
            css兼容性处理:postcss --> postcss-loader postcss-preset-env
    		帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式
        */
              			// 使用loader的默认配置
              			// 'postcss-loader',
             			// 修改loader的配置
                        { 
                            loader: 'postcss-loader', 
                            options: 
                            { 
                                ident: 'postcss', 
                                plugins: () => [ 
                                    // postcss 的插件 
                                    require('postcss-preset-env')() 
                                ] 
                            } 
                        }
                    ] 
                }]
        },
        plugins:[
            new HtmlWebpackPlugin({ 
                template: './src/index.html' 
            }), 
            new MiniCssExtractPlugin({
                filename: 'css/built.css'
            })
        ],
        mode: 'development' 
    };
    
  4. 修改 package.json

    "browserslist": { 
        // 开发环境 --> 设置node环境变量:process.env.NODE_ENV = development
        "development":[ 
            "last 1 chrome version", 
            "last 1 firefox version", 
            "last 1 safari version" 
        ],
        // 生产环境:默认是看生产环境
        "production": [ 
            ">0.2%", 
            "not dead", 
            "not op_mini all" 
        ] 
    }
    
  5. 运行指令: webpack

4.3 压缩 css

1.创建文件

  1. 下载安装包

npm install --save-dev optimize-css-assets-webpack-plugin

  1. 修改配置文件

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
    
    // 设置nodejs环境变量
    // process.env.NODE_ENV = 'development';
    
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/built.js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          {
            test: /\.css$/,
            use: [
              MiniCssExtractPlugin.loader,
              'css-loader',
              {
                loader: 'postcss-loader',
                options: {
                  ident: 'postcss',
                  plugins: () => [
                    // postcss的插件
                    require('postcss-preset-env')()
                  ]
                }
              }
            ]
          }
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html'
        }),
        new MiniCssExtractPlugin({
          filename: 'css/built.css'
        }),
        // 压缩css
        new OptimizeCssAssetsWebpackPlugin()
      ],
      mode: 'development'
    };
    
  2. 运行指令: webpack

4.4 js 语法检查

1.创建文件

  1. 下载安装包

    npm install --save-dev eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import

  2. 修改配置文件

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 
    const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin' );
    
    // 设置 nodejs 环境变量 
    // process.env.NODE_ENV = 'development';
    
    module.exports = {
        entry: './src/js/index.js',
        output: {
            filename: 'js/built.js',
        	path: resolve(__dirname, 'build')
      	},
      	module: {
            rules: [
          	/*
            	语法检查: eslint-loader  eslint
              	注意:只检查自己写的源代码,第三方的库是不用检查的
              	设置检查规则:
                	package.json中eslintConfig中设置~
                  	"eslintConfig": {
                    	"extends": "airbnb-base"
                  	}
                	airbnb --> eslint-config-airbnb-base  eslint-plugin-import eslint
          	*/
     	     {
        	      test: /\.js$/,
            	  exclude: /node_modules/,
    	          loader: 'eslint-loader',
        	      options: {
    	              // 自动修复eslint的错误
        	          fix: true
            	  }
         	 }
     	   ]
        },
        plugins: [
          new HtmlWebpackPlugin({
              template: './src/index.html'
          }), 
          new MiniCssExtractPlugin({ 
              filename: 'css/built.css' 
          }),
          // 压缩 css 
          new OptimizeCssAssetsWebpackPlugin()
        ],
        mode: 'development'
    };
    
  3. 配置 package.json

    "eslintConfig": { 
        "extends": "airbnb-base", 
        "env": {
            "browser": true 
        } 
    }
    
  4. 运行指令: webpack

4.5 js 兼容性处理

  1. 创建文件

  2. 下载安装包

    npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/polyfill core-js

  3. 修改配置文件

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      /*
        js兼容性处理:babel-loader @babel/core 
          1. 基本js兼容性处理 --> @babel/preset-env
            问题:只能转换基本语法,如promise高级语法不能转换
          2. 全部js兼容性处理 --> @babel/polyfill  
            问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了~
          3. 需要做兼容性处理的就做:按需加载  --> core-js
      */  
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          // 预设:指示babel做怎么样的兼容性处理
          presets: [
            [
              '@babel/preset-env',
              {
                // 按需加载
                useBuiltIns: 'usage',
                // 指定core-js版本
                corejs: {
                  version: 3
                },
                // 指定兼容性做到哪个版本浏览器
                targets: {
                  chrome: '60',
                  firefox: '60',
                  ie: '9',
                  safari: '10',
                  edge: '17'
                }
              }
            ]
          ]
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};
  1. 运行指令: webpack

4.6 js 压缩

  1. 创建文件

  2. 修改配置文件

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/built.js',
        path: resolve(__dirname, 'build')
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html'
        })
      ],
      // 生产环境下会自动压缩js代码
      mode: 'production'
    };
    
  3. 运行指令: webpack

4.7 HTML 压缩

  1. 创建文件

  2. 修改配置文件

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/built.js',
        path: resolve(__dirname, 'build')
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html',
          // 压缩html代码
          minify: {
            // 移除空格
            collapseWhitespace: true,
            // 移除注释
            removeComments: true
          }
        })
      ],
      mode: 'production'
    };
    
    
  3. 运行指令:webpack

4.8 生产环境配置(总)

  1. 修改配置文件

    const { resolve } = require('path');
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    // 定义nodejs环境变量:决定使用browserslist的哪个环境
    process.env.NODE_ENV = 'production';
    
    // 复用loader
    const commonCssLoader = [
      MiniCssExtractPlugin.loader,
      'css-loader',
      {
        // 还需要在package.json中定义browserslist
        loader: 'postcss-loader',
        options: {
          ident: 'postcss',
          plugins: () => [require('postcss-preset-env')()]
        }
      }
    ];
    
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/built.js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          /*
            正常来讲,一个文件只能被一个loader处理。
            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
              先执行eslint 在执行babel
          */
          {
            // 在package.json中eslintConfig --> airbnb
            test: /\.js$/,
            exclude: /node_modules/,
            // 优先执行
            enforce: 'pre',
            loader: 'eslint-loader',
            options: {
              fix: true
            }
          },
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    useBuiltIns: 'usage',
                    corejs: {version: 3},
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ]
            }
          },
          {
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]
      },
      plugins: [
        new MiniCssExtractPlugin({
          filename: 'css/built.css'
        }),
        new OptimizeCssAssetsWebpackPlugin(),
        new HtmlWebpackPlugin({
          template: './src/index.html',
          minify: {
            collapseWhitespace: true,
            removeComments: true
          }
        })
      ],
      mode: 'production'
    };
    
  2. 运行指令: webpack

5 章:webpack优化配置

5.0 zui前面

# webpack性能优化
* 开发环境性能优化
* 生产环境性能优化

## 开发环境性能优化
* 优化打包构建速度
  * HMR
* 优化代码调试
  * source-map

## 生产环境性能优化
* 优化打包构建速度
  * oneOf
  * babel缓存
  * 多进程打包
  * externals
  * dll
* 优化代码运行的性能
  * 缓存(hash-chunkhash-contenthash)
  * tree shaking
  * code split
  * 懒加载/预加载
  * pwa

5.1 HMR

  1. 创建文件

  2. 修改配置文件

    /*
    HMR: hot module replacement 热模块替换 / 模块热替换
        作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块)极大提升构建速度
        样式文件:可以使用HMR功能:因为style-loader内部实现了~
        js文件:默认不能使用HMR功能 --> 需要修改js代码,添加支持HMR功能的代码
        	注意:HMR功能对js的处理,只能处理非入口js文件的其他文件。
        html文件: 默认不能使用HMR功能.同时会导致问题:html文件不能热更新了~ (不用做HMR功能)
        	解决:修改entry入口,将html文件引入
    */
    
    //在开发环境配置基础上加
    
    ...
    module.exports = {
      ...
      devServer: {
        ...
        // 开启HMR功能
        // 当修改了webpack配置,新配置要想生效,必须重新webpack服务
        hot: true
      }
    };
    

    对js入口文件修改

    if (module.hot) {
      // 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效
      module.hot.accept('./print.js', function() {
        // 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。
        // 会执行后面的回调函数
        print();
      });
    }
    
  3. 运行指令: webpack

5.2 source-map

  1. 创建文件

  2. 修改配置文件

    ...
    module.exports = {
      ...
      devServer: {
        contentBase: resolve(__dirname, 'build'),
        compress: true,
        port: 3000,
        open: true,
        hot: true
      },
      devtool: 'eval-source-map'
    };
    
    /*
      source-map: 一种【提供源代码到构建后代码映射】技术 (如果构建后代码出错了,通过映射可以追踪源代码错误) 
    
        [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
    
        source-map:外部
          	错误代码准确信息 和 源代码的错误位置
        inline-source-map:内联
          	只生成一个内联source-map
          	错误代码准确信息 和 源代码的错误位置
        hidden-source-map:外部
          	错误代码错误原因,但是没有错误位置
          	不能追踪源代码错误,只能提示到构建后代码的错误位置
        eval-source-map:内联
          	每一个文件都生成对应的source-map,都在eval
          	错误代码准确信息 和 源代码的错误位置
        nosources-source-map:外部
          	错误代码准确信息, 但是没有任何源代码信息
        cheap-source-map:外部
          	错误代码准确信息 和 源代码的错误位置 
          	只能精确的行
        cheap-module-source-map:外部
          	错误代码准确信息 和 源代码的错误位置 
          	module会将loader的source map加入
    
        内联 和 外部的区别:
        		1. 外部生成了文件,内联没有 
        		2. 内联构建速度更快
    
        开发环境:速度快,调试更友好
          速度快(eval>inline>cheap>...)
            	eval-cheap-souce-map
            	eval-source-map
          调试更友好  
            	souce-map
            	cheap-module-souce-map
            	cheap-souce-map
          --> eval-source-map  / eval-cheap-module-souce-map
    
        生产环境:源代码要不要隐藏? 调试要不要更友好
          内联会让代码体积变大,所以在生产环境不用内联
          nosources-source-map 	全部隐藏
          hidden-source-map 	只隐藏源代码,会提示构建后代码错误信息
        --> source-map / cheap-module-souce-map
    */
    
  3. 运行指令: webpack

5.3 oneOf

  1. 创建文件

  2. 修改配置文件

    //在生产环境配置基础上加
    ...
    
    module.exports = {
      ...
      module: {
        rules: [
          {
            // 在package.json中eslintConfig --> airbnb
            test: /\.js$/,
            // 优先执行
            enforce: 'pre',
            ...
          },
          {
            // 以下loader只会匹配一个
            // 注意:不能有两个配置处理同一种类型文件
            oneOf: [
              {
                test: /\.css$/,
                use: [...commonCssLoader]
              },
              {
                test: /\.less$/,
                use: [...commonCssLoader, 'less-loader']
              },
              {
                test: /\.js$/,
                ...
              },
              {
                test: /\.(jpg|png|gif)/,
                ...
              },
              {
                test: /\.html$/,
                loader: 'html-loader'
              },
              {
                exclude: /\.(js|css|less|html|jpg|png|gif)/,
                ...
              }
            ]
          }
        ]
      },
      ...
    };
    
  3. 运行指令:webpack

5.4 缓存

  1. 创建文件

  2. 修改配置文件

    /*
      缓存:
        babel缓存
          cacheDirectory: true
          --> 让第二次打包构建速度更快
        文件资源缓存
          hash: 每次wepack构建时会生成一个唯一的hash值。
            问题: 因为js和css同时使用一个hash值。
              如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件)
          chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
            问题: js和css的hash值还是一样的
              因为css是在js中被引入的,所以同属于一个chunk
          contenthash: 根据文件的内容生成hash值。不同文件hash值一定不一样    
          --> 让代码上线运行缓存更好使用
    */
    
    ...
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/built.[contenthash:10].js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          {
            // 在package.json中eslintConfig --> airbnb
            test: /\.js$/,
            exclude: /node_modules/,
            // 优先执行
            enforce: 'pre',
            ...
          },
          {
            // 以下loader只会匹配一个
            // 注意:不能有两个配置处理同一种类型文件
            oneOf: [
              {
                test: /\.css$/,
                use: [...commonCssLoader]
              },
              {
                test: /\.less$/,
                use: [...commonCssLoader, 'less-loader']
              },
              /*
                正常来讲,一个文件只能被一个loader处理。
                当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
                  先执行eslint 在执行babel
              */
              {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: {
                  presets: [
                    ...
                  ],
                  // 开启babel缓存
                  // 第二次构建时,会读取之前的缓存
                  cacheDirectory: true
                }
              },
              {
                test: /\.(jpg|png|gif)/,
                ...
              },
              {
                test: /\.html$/,
                ...
              },
              {
                exclude: /\.(js|css|less|html|jpg|png|gif)/,
                ...
              }
            ]
          }
        ]
      },
      plugins: [
        new MiniCssExtractPlugin({
          filename: 'css/built.[contenthash:10].css'
        }),
        ...
      ],
      mode: 'production',
      devtool: 'source-map'
    };
    
  3. 栗子server.js

    /*
      服务器代码
      启动服务器指令:
        npm i nodemon -g
        nodemon server.js
    
        node server.js
      访问服务器地址:
        http://localhost:3000
    
    */
    const express = require('express');
    
    const app = express();
    // express.static向外暴露静态资源
    // maxAge 资源缓存的最大时间,单位ms
    app.use(express.static('build', { maxAge: 1000 * 3600 }));
    
    app.listen(3000);
    
  4. 运行指令: webpack

5.5 tree shaking

  1. 创建文件

  2. 配置文件

    /*
      tree shaking:去除无用代码
        前提:1. 必须使用ES6模块化  2. 开启production环境(生产环境配置)
        作用: 减少代码体积
    
        在package.json中配置 
          "sideEffects": false 所有代码都没有副作用(都可以进行tree shaking)
            问题:可能会把css / @babel/polyfill (副作用)文件干掉
          "sideEffects": ["*.css", "*.less"]
    */
    
  3. 运行指令: webpack

5.6 code split

分开打包,打包为一个体积较大

  1. 创建文件

  2. 修改配置文件(三种情况)

  3. demo1:多入口

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      // 单入口
      // entry: './src/js/index.js',
      entry: {
        // 多入口:有一个入口,最终输出就有一个bundle
        index: './src/js/index.js',
        test: './src/js/test.js'
      },
      output: {
        // [name]:取文件名
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build')
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html',
          minify: {
            collapseWhitespace: true,
            removeComments: true
          }
        })
      ],
      mode: 'production'
    };
    
    
  4. demo2:多入口且判断是否有重复

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: {
        index: './src/js/index.js',
        test: './src/js/test.js'
      },
      output: {
        // [name]:取文件名
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build')
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html',
          minify: {
            collapseWhitespace: true,
            removeComments: true
          }
        })
      ],
      /*
        1. 可以将node_modules中代码单独打包一个chunk最终输出
        2. 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk
      */
      optimization: {
        splitChunks: {
          chunks: 'all'
        }
      },
      mode: 'production'
    };
    
  5. demo3:单入口

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      // 单入口
      entry: './src/js/index.js',
      output: {
        // [name]:取文件名
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build')
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html',
          minify: {
            collapseWhitespace: true,
            removeComments: true
          }
        })
      ],
      /*
        1. 可以将node_modules中代码单独打包一个chunk最终输出
        2. 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk
      */
      optimization: {
        splitChunks: {
          chunks: 'all'
        }
      },
      mode: 'production'
    };
    
  6. 运行指令: webpack

5.7 lazy loading

栗子

//入口js文件
//如有一个test.js文件

document.getElementById('btn').onclick = function() {
  // 懒加载~:当文件需要使用时才加载~
  // import(/* webpackChunkName: 'test'*/'./test').then(({ mul }) => {
  //    console.log(mul(4, 5));
  //  });
    
  // 预加载 prefetch:会在使用之前,提前加载js文件 
  //				 等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
  // 正常加载:可以认为是并行加载(同一时间加载多个文件)  
  import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
    console.log(mul(4, 5));
  });
};

配置文件

//开发环境配置

5.8 pwa

  1. 创建文件

  2. 下载安装包

    npm install --save-dev workbox-webpack-plugin

  3. 修改配置文件

    /*
      PWA: 渐进式网络开发应用程序(离线可访问)
        workbox --> workbox-webpack-plugin
    */
    
    ...
    const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
    ...
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/built.[contenthash:10].js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          {
            // 在package.json中eslintConfig --> airbnb
            test: /\.js$/,
            ...
          },
          {
            // 以下loader只会匹配一个
            // 注意:不能有两个配置处理同一种类型文件
            oneOf: [...]
          }
        ]
      },
      plugins: [
        new MiniCssExtractPlugin({
          filename: 'css/built.[contenthash:10].css'
        }),
        new OptimizeCssAssetsWebpackPlugin(),
        new HtmlWebpackPlugin({...}),
        new WorkboxWebpackPlugin.GenerateSW({
          /*
            1. 帮助serviceworker快速启动
            2. 删除旧的 serviceworker
            生成一个 serviceworker 配置文件~
          */
          clientsClaim: true,
          skipWaiting: true
        })
      ],
      mode: 'production',
      devtool: 'source-map'
    };
    
    //index.js入口文件
    /*
      1. eslint不认识 window、navigator全局变量
        解决:需要修改package.json中eslintConfig配置
          "env": {
            "browser": true // 支持浏览器端全局变量
          }
       2. sw代码必须运行在服务器上
          --> nodejs
          -->
            npm i serve -g
            serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去
    */
    // 注册serviceWorker
    // 处理兼容性问题
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', () => {
        navigator.serviceWorker
          .register('/service-worker.js')
          .then(() => {
            console.log('sw注册成功了~');
          })
          .catch(() => {
            console.log('sw注册失败了~');
          });
      });
    }
    
  4. 运行指令:webpack

5.9 多进程打包

  1. 创建文件

  2. 下载安装包

    npm install --save-dev thread-loader

  3. 修改配置文件

    // 在前面生产环境配置及优化的基础上
    
    ...
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/built.[contenthash:10].js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          {
            // 在package.json中eslintConfig --> airbnb
            test: /\.js$/,
            exclude: /node_modules/,
            // 优先执行
            enforce: 'pre',
            ...
          },
          {
            // 以下loader只会匹配一个
            // 注意:不能有两个配置处理同一种类型文件
            oneOf: [
              //省略less css...
              {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                  /* 
                    开启多进程打包。 
                    进程启动大概为600ms,进程通信也有开销。
                    只有工作消耗时间比较长,才需要多进程打包
                  */
                  {
                    loader: 'thread-loader',
                    options: {
                      workers: 2 // 进程2个
                    }
                  },
                  {
                    loader: 'babel-loader',
                    options: {
                      presets: [...],
                      // 开启babel缓存
                      // 第二次构建时,会读取之前的缓存
                      cacheDirectory: true
                    }
                  }
                ]
              },
              //省略图、html...exclude: /\.(js|css|less|html|jpg|png|gif)/
              ...
            ]
          }
        ]
      },
      plugins: [
        new MiniCssExtractPlugin({
          filename: 'css/built.[contenthash:10].css'
        }),
        new OptimizeCssAssetsWebpackPlugin(),
        new HtmlWebpackPlugin({...}),
        new WorkboxWebpackPlugin.GenerateSW({...})
      ],
      mode: 'production',
      devtool: 'source-map'
    };
    
  4. 运行指令:webpack

5.10 externals

修改配置文件

//在html手动通过网址引入包,如jq忽略不被一起打包

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'production',
  externals: {
    // 拒绝jQuery被打包进来
    jquery: 'jQuery'
  }
};

5.11 dll

  1. 创建文件

  2. 修改配置文件

    法一:webpack.config.js

    运行指令:`webpack`
    
    //打包成不同的chunk,可以对代码进行单独打包
    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const webpack = require('webpack');
    const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
    
    module.exports = {
      entry: './src/index.js',
      output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html'
        }),
        // 告诉webpack哪些库不参与打包,同时使用时的名称也得变~
        new webpack.DllReferencePlugin({
          manifest: resolve(__dirname, 'dll/manifest.json')
        }),
        // 将某个文件打包输出去,并在html中自动引入该资源
        new AddAssetHtmlWebpackPlugin({
          filepath: resolve(__dirname, 'dll/jquery.js')
        })
      ],
      mode: 'production'
    };
    

    法二:webpack.dll.js

    运行指令:`webpack --config webpack.dll.js`
    
    /*
      使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
        当你运行 webpack 时,默认查找 webpack.config.js 配置文件
        需求:需要运行 webpack.dll.js 文件
          运行指令:
          	--> webpack --config webpack.dll.js
    */
    
    const { resolve } = require('path');
    const webpack = require('webpack');
    
    module.exports = {
      entry: {
        // 最终打包生成的[name] --> jquery
        // ['jquery'] --> 要打包的库是jquery
        jquery: ['jquery'],
      },
      output: {
        filename: '[name].js',
        path: resolve(__dirname, 'dll'),
        // 打包的库里面向外暴露出去的内容叫什么名字,哈希值可以使每次的名称都不一样
        library: '[name]_[hash]' 
      },
      plugins: [
        // 打包生成一个 manifest.json --> 提供和jquery映射
        new webpack.DllPlugin({
          name: '[name]_[hash]', 						// 映射库的暴露的内容名称
          path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
        })
      ],
      mode: 'production'
    };
    

6 **章:**webpack 配置详情

6.1 entry

  1. 创建文件

  2. 修改配置文件

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    /*
      entry: 入口起点
        1. string --> './src/index.js'
          单入口
          打包形成一个chunk。 输出一个bundle文件。
          此时chunk的名称默认是 main
        2. array  --> ['./src/index.js', './src/add.js']
          多入口
          所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
            --> 只有在HMR功能中让html热更新生效~
        3. object
          多入口
          有几个入口文件就形成几个chunk,输出几个bundle文件
          此时chunk的名称是 key
    
          --> 特殊用法
            {
              // 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
              index: ['./src/index.js', './src/count.js'], 
              // 形成一个chunk,输出一个bundle文件。
              add: './src/add.js'
            }
    */
    
    module.exports = {
      entry: {
        index: ['./src/index.js', './src/count.js'], 
        add: './src/add.js'
      },
      output: {
        filename: '[name].js',
        path: resolve(__dirname, 'build')
      },
      plugins: [new HtmlWebpackPlugin()],
      mode: 'development'
    };
    
  3. 运行指令:webpack

6.2 output

  1. 创建文件

  2. 修改配置文件

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: './src/index.js',
      output: {
        // 文件名称(指定名称+目录)
        filename: 'js/[name].js',
        // 输出文件目录(将来所有资源输出的公共目录)
        path: resolve(__dirname, 'build'),
        // 所有资源引入公共路径前缀 --> 'imgs/a.jpg' --> '/imgs/a.jpg'
        publicPath: '/',
        chunkFilename: 'js/[name]_chunk.js', // 非入口chunk的名称
        // library: '[name]', // 整个库向外暴露的变量名
        // libraryTarget: 'window' // 变量名添加到哪个上 browser
        // libraryTarget: 'global' // 变量名添加到哪个上 node
        // libraryTarget: 'commonjs'
      },
      plugins: [new HtmlWebpackPlugin()],
      mode: 'development'
    };
    
    
  3. 运行指令:webpack

6.3 module

  1. 创建文件

  2. 修改配置文件

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: './src/index.js',
      output: {
        filename: 'js/[name].js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          // loader的配置
          {
            test: /\.css$/,
            // 多个loader用use
            use: ['style-loader', 'css-loader']
          },
          {
            test: /\.js$/,
            // 排除node_modules下的js文件
            exclude: /node_modules/,
            // 只检查 src 下的js文件
            include: resolve(__dirname, 'src'),
            // 优先执行
            enforce: 'pre',
            // 延后执行
            // enforce: 'post',
            // 单个loader用loader
            loader: 'eslint-loader',
            options: {}
          },
          {
            // 以下配置只会生效一个
            oneOf: []
          }
        ]
      },
      plugins: [new HtmlWebpackPlugin()],
      mode: 'development'
    };
    
  3. 运行指令:webpack

6.4 resolve

  1. 创建文件

  2. 修改配置文件

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/[name].js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          {
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
          }
        ]
      },
      plugins: [new HtmlWebpackPlugin()],
      mode: 'development',
      // 解析模块的规则
      resolve: {
        // 配置解析模块路径别名: 优点简写路径 缺点路径没有提示
        alias: {
          $css: resolve(__dirname, 'src/css')
        },
        // 配置省略文件路径的后缀名
        extensions: ['.js', '.json', '.jsx', '.css'],
        // 告诉 webpack 解析模块是去找哪个目录
        modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
      }
    };
    
  3. 运行指令:webpack

6.5 dev server

  1. 创建文件

  2. 修改配置文件

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/[name].js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          {
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
          }
        ]
      },
      plugins: [new HtmlWebpackPlugin()],
      mode: 'development',
      resolve: {
        alias: {
          $css: resolve(__dirname, 'src/css')
        },
        extensions: ['.js', '.json', '.jsx', '.css'],
        modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
      },
      devServer: {
        // 运行代码的目录
        contentBase: resolve(__dirname, 'build'),
        // 监视 contentBase 目录下的所有文件,一旦文件变化就会 reload
        watchContentBase: true,
        watchOptions: {
          // 忽略文件
          ignored: /node_modules/
        },
        // 启动gzip压缩
        compress: true,
        // 端口号
        port: 5000,
        // 域名
        host: 'localhost',
        // 自动打开浏览器
        open: true,
        // 开启HMR功能
        hot: true,
        // 不要显示启动服务器日志信息
        clientLogLevel: 'none',
        // 除了一些基本启动信息以外,其他内容都不要显示
        quiet: true,
        // 如果出错了,不要全屏提示~
        overlay: false,
        // 服务器代理 --> 解决开发环境跨域问题
        proxy: {
          // 一旦devServer(5000)服务器接受到 /api/xxx 的请求,就会把请求转发到另外一个服务器(3000)
          '/api': {
            target: 'http://localhost:3000',
            // 发送请求时,请求路径重写:将 /api/xxx --> /xxx (去掉/api)
            pathRewrite: {
              '^/api': ''
            }
          }
        }
      }
    };
    
  3. 运行指令:webpack

6.6 optimization

  1. 创建文件

  2. 修改配置文件

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const TerserWebpackPlugin = require('terser-webpack-plugin')
    
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build'),
        chunkFilename: 'js/[name].[contenthash:10]_chunk.js'
      },
      module: {
        rules: [
          {
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
          }
        ]
      },
      plugins: [new HtmlWebpackPlugin()],
      mode: 'production',
      resolve: {
        alias: {
          $css: resolve(__dirname, 'src/css')
        },
        extensions: ['.js', '.json', '.jsx', '.css'],
        modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
      },
      optimization: {
        splitChunks: {
          chunks: 'all'
          // 默认值,可以不写~
          /* minSize: 30 * 1024, // 分割的chunk最小为30kb
          maxSiza: 0, // 最大没有限制
          minChunks: 1, // 要提取的chunk最少被引用1次
          maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量
          maxInitialRequests: 3, // 入口js文件最大并行请求数量
          automaticNameDelimiter: '~', // 名称连接符
          name: true, // 可以使用命名规则
          cacheGroups: {
            // 分割chunk的组
            // node_modules文件会被打包到 vendors 组的chunk中。--> vendors~xxx.js
            // 满足上面的公共规则,如:大小超过30kb,至少被引用一次。
            vendors: {
              test: /[\\/]node_modules[\\/]/,
              // 优先级
              priority: -10
            },
            default: {
              // 要提取的chunk最少被引用2次
              minChunks: 2,
              // 优先级
              priority: -20,
              // 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
              reuseExistingChunk: true
            } 
          }*/
        },
        // 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
        // 解决:修改a文件导致b文件的contenthash变化
        runtimeChunk: {
          name: entrypoint => `runtime-${entrypoint.name}`
        },
        minimizer: [
          // 配置生产环境的压缩方案:js和css
          new TerserWebpackPlugin({
            // 开启缓存
            cache: true,
            // 开启多进程打包
            parallel: true,
            // 启动source-map
            sourceMap: true
          })
        ]
      }
    };
    
  3. 运行指令:webpack