webpack4从入门到精通

340 阅读16分钟

使用模块化存在的问题:

1、ES Module 存在环境兼容问题,尤其是可爱的IE
2、模块文件过多,网络请求频繁
3、所有的前端资源都需要模块化

第 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 使用相应模式的配置
development是开发模式、production是生产模式

第 2 章: webpack 的初体验

2.1 初始化配置

1. 初始化 package.json

新建一个文件夹,用编辑器例如vscode,打开文件夹
在编辑器的命令行内输入指令: npm init

2. 下载并安装 webpack

在编辑里的命令行内,输入指令:
npm install webpack webpack-cli -g 【=》全局安装】
注:上面的命令安装完成后,就可以在自己电脑的任意位置使用webpack命令了,不安装也可以的
npm install webpack webpack-cli --save-dev 【=》项目内安装】

2.2 编译打包应用

1. 创建文件

在2.1的文件夹内新建2个目录一个是build 一个是src
然后在src目录下新建一个文件index.js index.js文件内的代码如下:

function add (x, y) {
  return x + y
}
console.log(add(1, 2))

2. 运行指令(没有全局安装webpack)

1、开发环境指令: npx webpack ./src/index.js -o ./build --mode=development 最终会在build目录内生成一个打包后的main.js文件

1、指令解析:webpack会以 ./src/index.js 为入口文件开始打包,打包后输出到 ./build/built.js;整体打包环境,是开发环境

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

2、生产环境指令: npx webpack ./src/index.js -o ./build --mode=production
最终会在build目录内生成一个打包后的main.js文件.里面的代码会被压缩

2、指令解析:webpack会以 ./src/index.js 为入口文件开始打包,打包后输出到 ./build/built.js;整体打包环境,是生产环境

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

3. 结论

(1)webpack 能够编译打包 js 和 json 文件。
(2)能将 es6 的模块化语法转换成浏览器能识别的语法。
(3)生产环境能压缩代码。

4. 问题

(1)不能编译打包 css、 img 等文件。
(2)不能将 js 的 es6 基本语法转化为 es5 以下语法。

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

3.1 创建配置文件

1. 创建文件 webpack.config.js

2. 配置内容如下

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

3. 运行指令: npx webpack

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

3.2 打包样式资源

1. 创建文件

2. 下载安装 loader 包

npm install --save-dev css-loader style-loader less-loader less

3. 修改配置文件webpack.config.js

// 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. 运行指令: npx webpack

如果出现下面的错误,说明是 css-loader 和 style-loader 、less-loader版本过高,换成较低的版本就可以正常打包了

3.3 打包 HTML 资源

1. 创建文件

2. 下载安装 plugin 包

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

3. 修改配置文件webpack.config.js

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'
};

4.运行指令: npx webpack

3.4 打包图片资源

1. 创建文件

2. 下载安装 loader 包

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

3. 修改配置文件webpack.config.js

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']
      },
      {
        // 问题: url-loader 默认处理不了 html 中 img 标签中的图片,所以要用html-loader来解决
        // 例如: <img src="./angular.jpg" alt="angular">
        // 处理图片资源
        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也可以正常解析img标签中的图片地址了
          // esModule: false,
          // 给图片进行重命名
          // [hash:10]取图片的 hash 的前 10 位
          // [ext]取文件原来扩展名
          name: '[hash:10].[ext]'
        }
      },
      {
        test: /\.html$/,
        // 处理 html 文件中 img标签的 图片 <img src="./angular.jpg" alt="angular">
        //负责引入 img, 从而能被 url-loader 进行处理
        loader: 'html-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

4. 运行指令: npx 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 资源以外的资源)
      // 使用file-loader
      {
        // 排除 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. 运行指令: npx webpack

3.6 devserver

1. 创建文件

和 3-5打包其他资源 的文件一样

2. 下载安装 webpack-ev-server 包

npm install --save-dev webpack-dev-server

3. 修改配置文件

1、不运行 npx webpack 直接运行 npx webpack-dev-server可以在界面展示数据
2、webpack-dev-server只会在内存中编译打包,不会有任何输出

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 资源以外的资源)
      // 使用file-loader
      {
        // 排除 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指令为:npx webpack-dev-server
  devServer: {
    // 项目构建后路径
    contentBase: resolve(__dirname, 'build'),
    // 启动 gzip 压缩
    compress: true,
    // 端口号
    port: 4000,
    // 自动打开浏览器
    open: true
  }
};

4. 运行指令: npx webpack-dev-server

3.7 开发环境的基本配置(对上面的所有内容,做一个整理)

1. 创建文件

2. 下载安装包 npm install --save-dev 包名

"css-loader": "^3.6.0",
"file-loader": "^6.2.0",
"html-loader": "^1.3.2",
"html-webpack-plugin": "^4.5.0",
"less": "^2.7.3",
"less-loader": "^5.0.0",
"style-loader": "^1.3.0",
"url-loader": "^4.1.1",
"webpack": "^4.26.1",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"

3. 修改配置文件

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  // 项目入口
  entry: './src/js/index.js',
  // 项目出口
  output: {
    filename: 'js/built.js', // 打包后生成的对应的入口js文件
    path: path.join(__dirname, 'built')
  },
  // 对应的loager
  module: {
    rules: [
      // 处理css的loader
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      // 处理less的loader
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      // 处理images的loader,
      // 但是url-loader不能处理 html文件内 img标签引用的图片,需要用html-loader来处理
      {
        test: /\.(png|jpg|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024, // 图片小于8kb 进行base64编码,输出到built.js内
          outputPath: 'images', // image文件打包后保存在build/images/内
          name: '[hash:10].[ext]'
        }

      },
      // 处理images的loader,
      // 但是url-loader不能处理 html文件内 img标签引用的图片,需要用html-loader来处理
      {
        test: /\.html$/,
        loader: 'html-loader'

      },
      // 处理其他类型文件的loader
      {
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
          outputPath: 'public', // 其他文件打包后保存在build/public/内
          name: '[hash:10].[ext]' // 打包后的文件名字显示10位的hash值
        }
      }
    ]
  },
  // 对应的plugins
  plugins: [
    // 处理html类型的文件
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  //模式
  mode: 'development',
  // mode:'production'
  devServer: {
    open: true,
    compress: true,
    port: 400,
    contentBase: path.join(__dirname, 'build')

  }
}

4. 运行指令: npx webpack 和 npx webpack-dev-server 查看效果

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

4.1 提取 css 成单独文件

1.创建文件

2.下载插件

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

3. 修改配置文件

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const miniCssExtractPlugin = require('mini-css-extract-plugin')


module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'build/js/built.js',
    path: path.join(__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: 'build/css/built.css'
    })
  ],
  mode: 'development'

}

4 .运行指令: npx webpack

4.2 css 兼容性处理

1.创建文件

2.下载插件

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');

// browserslist如果想使用 开发环境,需要设置node环境变量
// 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-loader postcss-preset-env

            postcss-preset-env 帮 postcss-loader 找到 package.json 中 browserslist 里面的配置
            通过配置加载指定的css兼容性样式

            browserslist的相关配置去npm搜索包名browserslist即可
            browserslist默认是走生产环境的配置
            mode: 'development'并不能影响browserslist的默认走向
            browserslist如果想使用 开发环境,需要设置node环境变量

            "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"
              ]
            }
          */
         
          // 使用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": {
  "development": [
  "last 1 chrome version",
  "last 1 firefox version",
  "last 1 safari version"
  ],
  "production": [
  ">0.2%",
  "not dead",
  "not op_mini all"
  ]
}

5. 运行指令: npx webpack

4.3 压缩 css

1.创建文件

2.下载插件

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

3. 修改配置文件webpack.config.js

在头部添加引用插件
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'
)

在plugins数组中添加使用
plugins: [
  new HtmlWebpackPlugin({
  template: './src/index.html'
  }),
  new MiniCssExtractPlugin({
  filename: 'css/built.css'
  }),
  // 在这里使用:压缩 css
  new OptimizeCssAssetsWebpackPlugin()
],

4. 运行指令: npx webpack

4.4 js 语法检查

1.创建文件

2.下载插件

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

3. 修改配置文件webpack.config.js

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: [
      /*
        语法检查: eslint-loader  eslint
          注意:只检查自己写的源代码,第三方的库是不用检查的
          设置检查规则:
            package.json中eslintConfig中设置~
              "eslintConfig": {
                "extends": "airbnb-base"
              }
            airbnb需要下载的有关插件:
            eslint-config-airbnb-base  eslint-plugin-import  eslint
            eslint-config-airbnb-base 是eslint的语法检查中的一种风格
      */
      {
        test: /\.js$/,
        exclude: /node_modules/, // 注意:只检查自己写的源代码,第三方的库是不用检查的
        loader: 'eslint-loader',
        options: {
          // 自动修复eslint的错误
          fix: true
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};


4. 配置 package.json

"eslintConfig": {
    "extends": "airbnb-base",
    "env": {
    "browser": true
    }
}

5. 运行指令: npx webpack

4.5 js 兼容性处理(把es6或者es6+的语法变成浏览器可识别的es5语法)

1.创建文件

2.下载插件

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

3. 修改配置文件webpack.config.js

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  不需要配置,直接在js文件内引入即可 
            问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了~
          3. 需要做兼容性处理的就做兼容性处理:按需加载  --> core-js
        一般都是1和3一块使用,来解决js代码的兼容性
        2和3不能同时使用
      */  
      {
        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'
};


4. 运行指令: npx webpack

4.6 js 压缩(生产环境下只需要把mode设置为production就可以了)

1.创建文件

2.下载插件

3. 修改配置文件webpack.config.js

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就可以了
  mode: 'production'
};

4. 运行指令: npx webpack

4.7 HTML 压缩

1.创建文件

2.下载插件

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

3. 修改配置文件webpack.config.js

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'
};

4. 运行指令: npx webpack

4.8 生产环境的基本配置(对上面的所有内容,做一个整理)

1. 创建文件

2. 下载安装包 npm install --save-dev 包名

"@babel/core": "^7.12.9",
"@babel/preset-env": "^7.12.7",
"babel-loader": "^8.2.1",
"core-js": "^3.7.0",
"css-loader": "^5.0.1",
"eslint": "^7.14.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-loader": "^4.0.2",
"eslint-plugin-import": "^2.22.1",
"file-loader": "^6.2.0",
"html-loader": "^1.3.2",
"html-webpack-plugin": "^4.5.0",
"less": "^3.12.2",
"less-loader": "^7.1.0",
"mini-css-extract-plugin": "^1.3.1",
"optimize-css-assets-webpack-plugin": "^5.0.4",
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0",
"url-loader": "^4.1.1",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12"

3. 修改配置文件webpack.config.js

const path = 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: 'build/js/built.js', // 文件保存在哪个目录下
    path: path.join(__dirname, 'build') // 文件输入目录
  },

  // loader
  module: {
    rules: [
      // 处理css的loader
      // 把css代码作为单独的文件从打包后的js文件内拿出来:MiniCssExtractPlugin.loader
      // 对css文件进行兼容性的处理:postcss-loader、postcss-preset-env
      // 对css文件进行压缩处理:optimize-css-assets-webpack-plugin
      {
        test: /\.css$/,
        use: [...commonCssLoader]
      },
      // 处理less的loader:less-loader
      {
        test: /\.less$/,
        use: [...commonCssLoader, 'less-loader']
      },
      /**
      * 正常来讲, 一个文件只能被一个 loader 处理。
        当一个文件要被多个 loader 处理, 那么一定要指定 loader 执行的先后顺序:
        先执行 eslint 在执行 babel
        添加一个属性:enforce 值为 pre
      */
      // 处理js的loader:
      // js的语法检查(eslint、eslint-loader、eslint-config-airbnb-base、 eslint-plugin-import)
      // js的兼容性处理(babel-loader、@babel/core、 @babel/preset-env、 core-js)
      // js代码的压缩(mode模式)
      // js的语法检查
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        enforce: 'pre', // 优先执行
        loader: 'eslint-loader',
        options: {
          fix: true // 语法有误,自动修复
        }
      },
      // js的兼容性处理
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            [
              '@babel/preset-env',
              {
                useBuiltIns: 'usage',
                corejs: { version: 3 },
                targets: {
                  chrome: '60',
                  firefox: '50'
                }
              }
            ]
          ]
        }
      },

      // 图片处理的loader 插件 url-loader
      {
        test: /\.(jpg|png|gif)/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          outputPath: 'images', // 图片打包后保存的位置
        }
      },

      // 处理其他文件类型的loader 插件 file-loader
      {
        exclude: /\.(js|css|less|html|jpg|png|gif)/,
        loader: 'file-loader',  // 原样输出
        options: {
          outputPath: 'public',
          name: '[hash:10].[ext]'
        }
      },

      // 处理html文件内img标签中引入的图片路径的loader,用插件:html-loader  插件url-loader不能处理这种类型的图片
      {
        test: /\.html$/,
        loader: 'html-loader'
      },
    ]
  },

  // plugins
  plugins: [
    // 把打包后的css样式,单独的从打包后的js文件内拿出来,作为单独的css样式文件
    new MiniCssExtractPlugin({
      filename: 'css/built.css'  // 单独的css样式文件存放的位置
    }),
    // 对css文件进行压缩处理
    new OptimizeCssAssetsWebpackPlugin(),

    new HtmlWebpackPlugin({
      // 以哪个html文件为模板,在build目录内,创建新的文件
      template: './src/index.html',
      // 对html文件进行压缩
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],

  // 模式 =》 生产模式下,js代码会被自动压缩的
  mode: 'production'
}

5. 修改文件package.json

"browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.2%",
      "not op_mini all"
    ]
  },
  "eslintConfig": {
    "extends": "airbnb-base",
    // eslint不认识 window、navigator全局变量
    // 解决:需要修改package.json中eslintConfig配置
    // 使用时候,需要把这3行注释删掉,否则报错
    "env": {
      "browser": true / 支持浏览器端全局变量
    }
  }

6. 运行指令: npx webpack

第 5 章: webpack 优化配置

5.1 HMR -基于devServer的,所以生产环境不能使用HMR

1. 问题所在

在开发模式下,启动了devServer后,有一个文件改变,就要对所有文件进行重新打包操作,比较耗时

2. HMR: hot module replacement 热模块替换 / 模块热替换

作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块)极大提升构建速度

样式文件:可以使用HMR功能:因为style-loader内部实现了~

js文件:默认不能使用HMR功能 --> 需要修改js代码,添加支持HMR功能的代码
注意:HMR功能对js的处理,只能处理非入口js文件的其他文件。

html文件: 默认不能使用HMR功能.同时会导致问题:html文件不能热更新了~ (不用做HMR功能) 解决:修改entry入口,将html文件引入

3. 修改webpack.config.js下的entry、devserver配置

entry: ['./src/js/index.js', './src/index.html'],
devServer: {
  contentBase: resolve(__dirname, 'build'),
  compress: true,
  port: 3000,
  open: true,
  // 开启 HMR 功能
  // 当修改了 webpack 配置, 新配置要想生效, 必须重启 webpack 服务
  hot: true
}

4. js文件默认不能使用HMR功能 --> 需要修改js文件里面的代码,代码如下

print.js文件的内容

function print() {
  const content = 'hello print';
  console.log(content);
}

export default print;

index.js文件的内容

// 引入print.js文件// 引入print.js文件
import print from './print';
print();

// js文件使用HMR功能
if (module.hot) {
  // 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效
  module.hot.accept('./print.js', function() {
    // 方法会监听 print.js 文件的变化,一旦发生变化, print.js文件就会被重新加载
    // 但是其他模块不会重新打包构建
    print(); // print.js文件里面暴漏的方法
  });
}

5.2 source-map

1. source-map是什么?

是一种 提供源代码到构建后代码映射 技术 (如果构建后代码出错了,通过映射可以追踪源代码错误)

2. source-map有哪些类型可以配置?

[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

  • 1、source-map:外部   ----能够显示 错误代码的准确信息 和 源代码的准确错误位置
  • 2、inline-source-map:内联
    -----只生成一个内联source-map  
    -----能够显示 错误代码的准确信息 和 源代码的准确错误位置
  • 3、hidden-source-map:外部
    -----能够显示 错误代码的准确信息,但是不能显示源代码的错误位置
    -----不能追踪源代码错误,只能提示到构建后代码的错误位置
  • 4、eval-source-map:内联
    -----每一个文件都生成对应的source-map,都在eval内
    -----能够显示 错误代码的准确信息 和 源代码的准确错误位置
  • 5、nosources-source-map:外部
    -----能够显示 错误代码的准确信息, 但是找不到对应的任何源代码信息
  • 6、cheap-source-map:外部
    -----能够显示 错误代码的准确信息 和 源代码的准确错误位置
    -----只能精确到行
  • 7、cheap-module-source-map:外部
    -----能够显示 错误代码的准确信息 和 源代码的准确错误位置
    -----module会将loader的source map加入
  • 8、内联 和 外部的区别: -----1. 外部生成了文件,内联没有 2. 内联构建速度更快
  • 9、开发环境选择souce-map:速度快,调试更友好的souce-map
    -----速度快(eval>inline>cheap>...)
    -------eval-cheap-souce-map
    -------eval-source-map
    -----调试更友好
    -------souce-map
    -------cheap-module-souce-map
    -------cheap-souce-map
  • 10、开发环境souce-map的最终选择: -----eval-source-map / eval-cheap-module-souce-map
  • 11、生产环境选择souce-map:源代码要不要隐藏? 调试要不要更友好?   -------内联会让代码体积变大,所以在生产环境不用内联
    -------nosources-source-map 全部隐藏
    -------hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
  • 12、生产环境souce-map的最终选择【针对调试更友好来说】: ------source-map / cheap-module-souce-map

3. 修改配置文件webpack.config.js

devtool: 'eval-source-map'devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true,
    hot: true // 开启HMR功能
  },
devtool: 'source-map' // 只要写上这一段代码,即可开启source-map

5.3 oneOf -在生产环境中使用,开发环境没有必要使用

1. oneOf是用来干什么的?

1、oneOf用来提升构建的打包速度的
2、oneOf是针对webpack配置里面的loader来进行处理的
3、oneOf里面的loader只会匹配一个
4、oneOf里面不能有两个loader处理同一种类型文件

2. 修改配置文件webpack.config.js

// loader
  module: {
    rules: [
      // 因为oneOf里面不能有两个loader处理同一种类型文件
      // 又因为js的语法检查的loader 比 js的兼容性处理的loader 先执行
      // 所以把js的语法检查的loader从oneOf里面拿出来了
      // js的语法检查
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        enforce: 'pre', // 优先执行
        loader: 'eslint-loader',
        options: {
          fix: true // 语法有误,自动修复
        }
      },
      // oneOf被对象包裹,oneOf自身传递一个数组,数组内是对应的loader
      // oneOf里面的loader只会匹配一个  
      // oneOf里面不能有两个loader处理同一种类型文件 
      {
        oneOf: [
          // 处理css的loader
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          // 处理less的loader:less-loader
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          
          // 处理js的loader:
          // js的兼容性处理
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    useBuiltIns: 'usage',
                    corejs: { version: 3 },
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ]
            }
          }
        ]
      }
    ]
  },

5.4 缓存

缓存的时间是在服务端设置的,webpack配置文件是可以设置开启缓存的

1. babel缓存-修改配置文件webpack.config.js

 // loader
  module: {
    rules: [
      // js的兼容性处理
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            [
              '@babel/preset-env',
              {
                useBuiltIns: 'usage',
                corejs: { version: 3 },
                targets: {
                  chrome: '60',
                  firefox: '50'
                }
              }
            ]
          ],
          // 开启 babel 缓存
          // 第二次构建时, 会读取之前的缓存
          // 让第二次打包构建速度更快
          cacheDirectory: true
        }
      }
    ]
  },

2. 文件资源缓存-修改配置文件webpack.config.js

(1)hash: 每次wepack构建时会生成一个唯一的hash值。

问题:因为js和css同时使用一个hash值 (webpack打包时候自动生成的hash值)
问题:如果webpack重新打包,就会生成新的hash值,从而导致所有文件缓存失效(例如:我只修改一个文件)

(2)chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样

问题:js和css的hash值还是一样的
原因:因为css是在js中被引入的,所以同属于一个chunk

(3)contenthash: 根据文件的内容生成hash值。不同文件hash值一定不一样

可以让代码上线运行缓存更好使用

(4)修改配置文件webpack.config.js
module.exports = {
  output: {
  	// 文件资源缓存核心配置位置:使用contenthash
    filename: 'js/built.[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new MiniCssExtractPlugin({
      // 文件资源缓存核心配置位置:使用contenthash
      filename: 'css/built.[contenthash:10].css'
    })
  ],
};

5.5 tree shaking(也可以叫树摇)

1. tree shaking:去除无用代码

前提:1. 必须使用ES6模块化 2. 开启production环境
作用: 减少代码体积

2. 在package.json中配置

"sideEffects": false 所有代码都没有副作用(都可以进行tree shaking)
问题:不同的webpack版本中的tree shaking结果可能不同
问题:有的webpack版本中的tree shaking,可能会把css / @babel/polyfill (副作用)文件干掉

解决办法:"sideEffects": [".css", ".less"]

5.6 code split (代码分割成多个单独文件)

code split主要是对js代码进行分割

1. code split的方式一 => 修改配置文件webpack.config.js

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]:取文件名,对应的是entry内的key
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

2. code split的方式二 => 修改配置文件webpack.config.js

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

module.exports = {
  // 单入口
  // entry: './src/js/index.js',
  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'
};

3. code split的方式三 =>import动态导入语法

(1)修改配置文件webpack.config.js
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // 单入口
  entry: './src/js/index.js',
  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'
};

(2)修改js文件里的代码

在index.js文件内,使用import动态导入语法,导入了test.js文件
然后就可以在index.js文件内 使用 test.js 中暴露出来的方法了
同时webpack打包后会生成2个单独的js文件

index.js文件内容

function sum(...args) {
  return args.reduce((p, c) => p + c, 0);
}
console.log(sum(1, 2, 3, 4));

/*
  通过js代码,让某个文件(test.js)被单独打包成一个chunk
  import动态导入语法:能将某个文件单独打包
  /* webpackChunkName: 'test' */'./test' 修改打包后的文件名字叫做test
*/
import(/* webpackChunkName: 'test' */'./test')
  .then(({ mul, count }) => {
    // 文件加载成功~
    console.log(mul(2, 5));
  })
  .catch(() => {
    console.log('文件加载失败~');
  });


test.js文件内容


export function mul(x, y) {
  return x * y;
}

export function count(x, y) {
  return x - y;
}

5.7 lazy loading(js文件的懒加载)

  • 懒加载:当文件需要使用时才加载
  • 预加载: 会在使用之前,提前加载js文件
  • 正常加载可以认为是并行加载(同一时间加载多个文件)
  • 预加载:等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
  • 预加载的浏览器兼容性会存在问题例如:IE浏览器
  • 懒加载可以看成是和 code split的方式三 =>import动态导入语法 一样

1. webpack.config.js的文件配置

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

module.exports = {
  // 单入口
  entry: './src/js/index.js',
  output: {
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },
  mode: 'production'
};

2. index.js的文件内容

console.log('index.js文件被加载了~');

// import { mul } from './test';

document.getElementById('btn').onclick = function() {
  // 懒加载可以看成是和 code split的方式三 =>import动态导入语法 一样
  import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
    console.log(mul(4, 5));
  });
};

3. test.js的文件内容

console.log('test.js文件被加载了~');

export function mul(x, y) {
  return x * y;
}

export function count(x, y) {
  return x - y;
}

4. index.html的文件内容

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webpack</title>
</head>

<body>
  <h1>hello lazy loading</h1>
  <button id="btn">按钮</button>
</body>

</html>

5.8 PWA

5.9 多进程打包

主要是给 js的兼容性处理的loader【babel-loader】来使用的

1. 下载安装包

npm install --save-dev thread-loader

2. 修改webpack.config.js配置文件

 // loader
module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      use: [
        /* 
          开启多进程打包。 
          进程启动大概为600ms,进程通信也有开销。
          只有工作消耗时间比较长,才需要多进程打包
          注意:thread-loader 一定要放在其他的loader的后面才行
        */
        {
          loader: 'thread-loader', // thread-loader 多进程打包的插件
          options: {
            workers: 2 // 进程2个
          }
        },
        {
          loader: 'babel-loader',
          options: {
            presets: [
              [
                '@babel/preset-env',
                {
                  useBuiltIns: 'usage',
                  corejs: { version: 3 },
                  targets: {
                    chrome: '60',
                    firefox: '50'
                  }
                }
              ]
            ],
            // 开启babel缓存
            // 第二次构建时,会读取之前的缓存
            cacheDirectory: true
          }
        }
      ]
    }
  ]
}

5.10 externals (拒绝哪些第三方包,打包进来,这些包可以用CDN的方式引用)

1. 修改webpack.config.js配置文件

mode: 'production',
// 主要代码在这里
externals: {
  // 拒绝 jQuery 被打包进来
  jquery: 'jQuery'
}

5.10 dll =>动态链接库【有点难懂哦!!!】

1. 在项目根目录创建一个文件webpack.dll.js

2. 修改webpack.dll.js配置文件

/*
  使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
    当你运行 webpack 时,默认查找 webpack.config.js 配置文件
    需求:需要运行 webpack.dll.js 文件
    解决方法,运行命令:npx 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'
};

3. 运行命令: npx webpack --config webpack.dll.js

会生成 dll文件夹,里面有对应的打包的文件

4. 下载插件

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

5. 修改webpack.config.js配置文件

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'
}

5.11 externals 和 dll 的不同

externals
1、只需要在webpack.config.js文件内进行配置即可,不需要打包

2、拒绝哪些第三方包,打包进来,这些包可以用CDN的方式在打包后的html页面引用

dll(动态链接库)
1、需要自己手动写配置代码去打包,实现起来麻烦 ,只需要打包一次就可以了

2、使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包

3、还需要在webpack.config.js文件内进行配置才行

5.12 webpack性能优化的简单总结

webpack性能优化

  • 开发环境性能优化
  • 生产环境性能优化

开发环境性能优化

  • 优化打包构建速度
    • HMR
  • 优化代码调试
    • source-map

生产环境性能优化

  • 优化打包构建速度
    • oneOf
    • babel缓存
    • 多进程打包
    • externals
    • dll
  • 优化代码运行的性能
    • 缓存文件(hash、chunkhash、contenthash)
    • tree shaking
    • code split
    • 懒加载/预加载
    • PWA

第 6 章: webpack 配置详情

6.1 entry

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
4. 特殊用法
{
  // 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
  index: ['./src/index.js', './src/count.js'], 
  // 形成一个chunk,输出一个bundle文件。
  add: './src/add.js'
}

6.2 output

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'
    // <img src="imgs/a.jpg"/> --> <img src="/imgs/a.jpg"/> 
    publicPath: '/', // 通常用在生产模式中
    chunkFilename: 'js/[name]_chunk.js', // 非入口chunk的名称
    // library: '[name]', // 整个库向外暴露的变量名
    // libraryTarget: 'window' // 变量名添加到哪个上 browser
    // libraryTarget: 'global' // 变量名添加到哪个上 node
    // libraryTarget: 'commonjs' // 以commonjs规范向外部暴露库
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development'
}

6.3 module

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',
        // loader 对应的配置选项
        options: {}
      },
      {
        // 以下配置只会生效一个
        oneOf: []
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development'
};

6.4 resolve =》解析模块规则用的

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: {
    // 配置解析模块路径别名: 优点简写路径 缺点路径没有提示
    // 我们写代码的时候,不需要使用import('../../b.css')了
    // 直接用 import('$css.b.css') 即可
    alias: {
      $css: resolve(__dirname, 'src/css')
    },
    
    // 配置省略文件路径的后缀名
    // 我们写代码的时候,不需要使用const b = import('../../b.js')了
    // 直接用 const b = import('../../b') 即可
    extensions: ['.js', '.json', '.jsx', '.css'],
    
    // 告诉 webpack 解析模块是去找哪个目录
    modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
  }
};

6.5 devServer => 只能用于开发环境

const { resolve } = require('path');

module.exports = {

  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': ''
        }
      }
    }
  }
}

6.6 optimization => 在生产环境下才有意义

const TerserWebpackPlugin = require('terser-webpack-plugin')

module.exports = {
 
  mode: 'production',

  optimization: {
  	usedExports:true,// 树摇的时候可以用到,负责标记[枯树叶]
    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}`
    },
    // minimize: true 告知 webpack 使用 TerserPlugin 或其它在 optimization.minimizer 定义的插件压缩 bundle
    minimize: true, // 树摇的时候可以用到,负责摇掉[枯树叶]
    minimizer: [
      // 配置生产环境的压缩方案:js和css
      new TerserWebpackPlugin({
        // 开启缓存
        cache: true,
        // 开启多进程打包
        parallel: true,
        // 启动source-map
        sourceMap: true
      })
    ]
  }
};

webpack的面试题的友情链接