精心整理Webpack7分钟极速回忆录

9/14/2021 Webpack

精心整理 Webpack7 分钟极速回忆录

# 一、安装 Node Webpack Webpack-cli

# 1.1 Node 去官网下载安装

# 1.2 全局安装 webpack webpack-cli

当你 React 项目用的是 webpack 5.x, Vue 项目用的是 4.x, Angular 用的是 3.x

此时你就需要局部安装,不然多个项目共用一个全局的 webpack 时,就会出现版本冲突问题。

// 全局安装
npm i webpack webpack-cli -g
// 推荐:局部安装,需要先 npm init -y 初始化一个包管理器
npm i webpack webpack-cli -D // -D 等价 --save-dev 开发依赖
1
2
3
4

本文截止 2021 年 9 月 7 日 23:44:25 版本信息为

名称 版本
Webpack v5.52.0
Webpack-Cli v4.8.0
Node v14.16.1
Npm v7.20.6

# 二、初识 Webpack

# 2.1 官方文档地址

英文官网:https://webpack.js.org (opens new window) 中文官网 V4:https://v4.webpack.docschina.org (opens new window) 中文官网 V5:https://webpack.docschina.org (opens new window)

# 2.2 入口

webapck 默认入口文件为 src/index.js

通过 entry 可以修改入口位置

module.exports = {
  entry: "./src/index.js", // 默认配置
};
1
2
3

# 2.3 出口

webapck 默认出口文件为 dist/main.js

通过 output.filename 可以修改出口位置

module.exports = {
  output: {
    filename: "./dist/main.js", // 默认配置
  },
};
1
2
3
4
5

# 2.4 第一次打包 执行 webapck

注: 使用 npx webpack 来使用局部的 webpack

原文件目录:

index.html 引入 src/index.js

src/index.js 引入 format.js And math.js

├── index.html
├── package.json
└── src
   ├── index.js
   └── js
      ├── format.js
      └── math.js
1
2
3
4
5
6
7

最终在控制台输入webpack,则生成以下目录

├── dist
|  └── main.js
├── index.html
├── package.json
└── src
   ├── index.js
   └── js
      ├── format.js
      └── math.js
1
2
3
4
5
6
7
8
9

此时注意,index.html并没有自动引入dist/main.js,原因是需要配置插件,本文后边会做说明。

当前,你可以在index.html手动引入dist/main.js看一下js文件有没有被正常打包。

# 2.4 package.json 脚本

package.json文件的scripts中配置对应脚本,使用npm run即可运行指定的脚本,注意,在此处执行的命令默认是都会从node_modules/.bin下去查找命令的,即使用的是局部的版本。

"scripts": {
  "build": "webpack"
},
1
2
3

# 2.5 使用配置文件

配置文件的默认名称是 webpack.config.js,如果你的项目根目录存在该文件,则webpack在打包的时候会自动载入该配置文件,该文件的基本导出语法如下:

注:webpack 是在 Node 环境下运行,Node 使用的是 CommonJS 规范,所以需使用 module.exports

// 引入Node内置模块
const path = require("path");

// 导出配置信息
module.exports = {
  // 入口
  entry: "./src/index.js",
  output: {
    // 出口,必须是绝对路径
    // __dirname能拿到当前文件的路径信息(不含文件名称)
    // 再用 path.resolve() 可以拼接出绝对路径
    path: path.resolve(__dirname, "./dist"),
    // 指定输出的文件名称
    filename: "bundle.js",
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 2.6 指定配置文件的名称

如果你不想使用 webpack.config.js这个名称,可以在package.json文件的启动脚本处指定配置文件,具体如下:

--config 后边就是你的自定义文件名称

"scripts": {
  "build": "webpack --config wb.config.js"
}
1
2
3

# 三、webpack 配置 loader

# 3.1 配置 CSS

正确显示 CSS 需要配置两个 loader,分别是style-loader css-loader

css-loader只是负责将.css文件进行解析,并不会将解析之后的 css 插入到页面中;

如果我们希望再完成插入 style 的操作,那么我们还需要另外一个loader,就是style-loader

# 3.1.1 安装

npm i style-loader css-loader -D

# 3.1.2 配置

具体配置如下:

const path = require("path");

module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "bundle.js",
  },
  module: {
    // 配置loader,用于处理 css,js,图片,等等几乎所有的资源
    rules: [
      {
        //正则匹配指定的文件类型,\. 是因为 .在正则中具有特殊含义,需用 \ 转义
        test: /\.css$/,

        // 注意:因为loader的执行顺序是从右向左(或者说从下到上,或者说从后到前的),所以我们需要将style-loader写到css-loader的前面。

        // 语法一(语法糖):当只有一个loader时可以简写为:
        // loader:'css-loader'
        // 语法二(语法糖):当不需要传递参数时多个loader可以简写:
        // use:['style-loader','css-loader']
        // 语法三:完整语法
        use: [
          {
            loader: "style-loader",
            // 这里还能给loader传递一些参数 当前不需要
            // options: ...
          },
          { loader: "css-loader" },
        ],
      },
    ],
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 3.2 配置 less

同样的道理,需要使用 loader 来处理,这里使用到一个lessless-loader.

# 3.2.1 安装

npm i less less-loader -D

# 3.2.2 配置

const path = require("path");

module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: "style-loader",
          },
          { loader: "css-loader" },
        ],
      },
      // 这里需要新建一个对象,用来匹配 less文件,
      {
        test: /\.less$/,
        // less文件通过 less-loader 转化为css文件,
        // css文件通过 css-loader 正确解析后,交给 style-loader 插入到页面中
        use: ["style-loader", "css-loader", "less-loader"],
      },
    ],
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 3.3 PostCSS 工具

什么是 PostCSS 呢?

  • PostCSS 是一个通过 JavaScript 来转换样式的工具;
  • 这个工具可以帮助我们进行一些 CSS 的转换和适配,比如自动添加浏览器前缀、css 样式的重置;
  • 但是实现这些功能,我们需要借助于 PostCSS 对应的插件;

如何使用 PostCSS 呢?主要就是两个步骤:

  • 第一步:查找 PostCSS 在构建工具中的扩展,比如 webpack 中的 postcss-loader;
  • 第二步:选择可以添加你需要的 PostCSS 相关的插件;

通过 PostCSS 你可以实现以下效果:

/* 处理前 */
div {
  /* CSS最新特性 八位代表rgba,大多数浏览器不能识别 */
  color: #12345678;
  user-select: none;
}

/* 处理后 */
div {
  color: rgba(18, 52, 86, 0.47059);
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

webpack中使用postcss大概思路:

postcss-loader => postcss => 查找 postcss 的插件实现对应功能

postcss-loader => postcss => 预设(本身也是插件,集成诸多功能在一起叫它预设)

常用插件(陆续补充):

插件名 作用 安装
autoprefixer 自动添加浏览器前缀 npm i autoprefixer -D

# 3.3.1 安装

插件方式使用 (不推荐,很少用 功能单一配置麻烦):

npm i postcss postcss-loader autoprefixer -D
1

预设方式使用(推荐,包含诸多功能,且内置 autoprefixer):

npm i postcss postcss-loader postcss-preset-env -D
1

# 3.3.2 配置详情

const path = require("path");

module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: "style-loader",
          },
          { loader: "css-loader" },
          {
            loader: "postcss-loader",
            options: {
              // 传入 postcss-loader 配置
              postcssOptions: {
                plugins: [
                  // 插件
                  // require("autoprefixer")
                  // 预设(本身也是插件,集成在一起叫它预设)
                  require("postcss-preset-env"),
                ],
              },
            },
          },
        ],
      },
      {
        test: /\.less$/,
        use: ["style-loader", "css-loader", "less-loader"],
      },
    ],
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 3.4 处理图片资源 与 base64

使用 file-loader 或者 url-loader url-loader 和 file-loader 的工作方式是相似的,但是可以将较小的文件,转成 base64 的 URI。

注意:最新版的css-loader也会对我们的图片资源打包,如果不想用css-loader的图片打包功能,可把它关闭,或者安装之前的 css-loader 版本 npm i css-loader@5.0.1 -D

关闭 css-loader 图片打包功能:

{
  test: /\.css$/,
  use: [
    "style-loader",
    {
      loader: "css-loader",
      options: {
        url: false,
      },
    },
  ],
}
1
2
3
4
5
6
7
8
9
10
11
12

# 3.4.1 安装

npm i url-loader -D

# 3.4.2 配置

const path = require("path");

module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          (loader: "style-loader"),
          (loader: "css-loader"),
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [require("postcss-preset-env")],
              },
            },
          },
        ],
      },
      {
        test: /\.less$/,
        use: ["style-loader", { loader: "css-loader" }, "less-loader"],
      },
      {
        test: /\.(png|jpe?g|gif|svg)$/,
        use: [
          {
            loader: "url-loader",
            options: {
              name: "img/[name]_[hash:6].[ext]",
              limit: 100 * 1024,
            },
          },
        ],
      },
    ],
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# 3.5 Webpack5 打包静态资源 实现 url-loader 效果

不需要下载任何loader webpack 打包相关资源:asset module type (opens new window)

{
  test: /\.(png|jpe?g|gif|svg)$/,
  type: "asset",
  generator: {
    filename: "img/[name]_[hash:6][ext]",
  },
  parser: {
    dataUrlCondition: {
      maxSize: 100 * 1024,
    },
  },
},
1
2
3
4
5
6
7
8
9
10
11
12

# 3.6 加载 iconfont 字体文件

Webpack4 版本:

{
  test: /\.(eot|ttf|woff2?)$/,
  use: {
    loader: "file-loader",
    options: {
      name: "font/[name]_[hash:6].[ext]",
    },
  },
}
1
2
3
4
5
6
7
8
9

Webpack5 版本:

{
  test: /\.ttf|eot|woff2?$/i,
  type: "asset/resource",
  generator: {
    filename: "font/[name].[hash:6][ext]"
  }
}
1
2
3
4
5
6
7

# 四、插件

# 4.1 CleanWebpackPlugin 删除打包文件夹

安装: npm install clean-webpack-plugin -D

配置:

const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
  ...
  plugins: [new CleanWebpackPlugin()],
};
1
2
3
4
5

# 4.2 HtmlWebpackPlugin 指定 HTML 文件模板

安装:npm install html-webpack-plugin -D

配置:

const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  plugins: [
    ...new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: "www.bookbook.cc",
      // 指定模板
      template: "./index.html",
    }),
  ],
};
1
2
3
4
5
6
7
8
9
10
11
12
13

注:在 HTML 文件中,可以使用 ejs 语法读取到传入给HtmlWebpackPlugin的值。 上边的 title 和 template 的值都可以在定义的模板中获取,如下:

<title><%= htmlWebpackPlugin.options.template %></title>
1

# 4.3 DefinePlugin 定义全局常量

DefinePlugin 允许在编译时创建配置的全局常量,是一个 webpack 内置的插件(不需要单独安装)

使用定义的 全局常量和上述方式一样,都是 ejs <% %> 语法

const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { DefinePlugin } = require("webpack");


module.exports = {
  ...
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: "www.bookbook.cc",
      // 指定模板
      template: "./index.html",
    }),
    new DefinePlugin({
      // 注意这里外层引号会被无视:BASE_URL: 'abc' 会把 abc的值填入这里,所以需要两层引号
      BASE_URL: "'./'",
    }),
  ],
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 4.4 CopyWebapckPlugin 复制文件夹到打包目录

实现类似vue项目打包时的public文件夹复制 安装:npm i copy-webpack-plugin --save-dev

const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { DefinePlugin } = require("webpack");
const CopyWebapckPlugin = require("copy-webpack-plugin");

module.exports = {
  ...
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: "webpack示例",
      // 指定模板
      template: "./public/index.html",
    }),
    new DefinePlugin({
      BASE_URL: '"./"',
    }),
    // 复制文件夹
    new CopyWebapckPlugin({
      // patterns 匹配
      patterns: [
        {
          // 从哪里复制
          from: "./public",
          // 复制到打包后的什么地方
          to: "public",
          globOptions: {
            // 需要忽略的文件
            ignore: ["**/index.html"],
          },
        },
      ],
    }),
  ],
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 五、支持 Vue3

安装:

npm i style-loader css-loader vue@next vue-loader@next @vue/compiler-sfc -D
1

配置:

const path = require("path");

const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader/dist/index");
const { DefinePlugin } = require("webpack");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.vue$/i,
        use: "vue-loader",
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new VueLoaderPlugin(),
    new DefinePlugin({
      // 是否用到options API,正确设置以方便 tree-shaking
      __VUE_OPTIONS_API__: true,
      // 浏览器 devtools
      __VUE_PROD_DEVTOOLS__: true,
    }),
  ],
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 六、开发服务器

# 6.1 watch 选项

webpack 给我们提供了 watch 模式:

  • 在该模式下,webpack 依赖图中的所有文件,只要有一个发生了更新,那么代码将被重新编译;
  • 我们不需要手动去运行 npm run build 指令了

如何开启 watch 呢?两种方式:

  • 方式一:在导出的配置中,添加 watch: true;
  • 方式二:在启动 webpack 的命令中,添加 --watch 的标识;

在启动脚本中加入:

"build": "webpack --watch"
1

# 6.2 devServer

安装:npm i webpack-dev-server -D

创建新启动命令:

"scripts": {
  "serve": "webpack serve",
}
1
2
3

注意:你需要先配置 HtmlWebpackPlugin 插件指定 HTML 模板,不然直接执行该命令会因为没有 index.html,导致预览的时候出现 404

devServer 其他配置:

module.exports = {
  ...
  devServer: {
	  // 如果希望其他地方可以访问,可以设置为 0.0.0.0
	  host: "localhost", // 默认值 本质是一个域名,会被解析成 127.0.0.1
	  // HMR 模块热替换,不刷新页面更改部分内容
	  hot: true,
	  port: 7070,
	  // 自动打开浏览器 webpack serve --open 一样的
	  open: true,
	  // 开启 Gzip压缩
	  compress: true,
	  // 代理服务器
	  proxy: {
	    "/api": {
	      target: "http://localhost:8888",
	      // 重写路径
	      pathRewrite: {
	        "^/api": "",
	      },
	      // 跨域
	      changeOrigin: true,
	    },
	  },
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 七、resolve 解析设置

常用于配置别名,自动识别扩展名

const path = require("path");
module.exports = {
  ...
  resolve: {
    // 别名
    alias: {
      // 对应的路径需要是绝对路径
      "@": path.resolve(__dirname, "./src"),
    },
    // 自动解析扩展名
    extensions: [".mjs", ".js", ".json", ".vue"],
    // 引入模块时的默认查找目录
    modules: ["node_modules"],
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

最后,如果你觉得该文章对你有所帮助的话

请点个赞支持一下 🐷(一个赞都没有,太可怜了)

# 八、我的 webpack.config.js

const path = require("path");

// 删除之前打包的文件夹插件
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
// 定于全局常量插件
const { DefinePlugin } = require("webpack");
// 解析处理.vue插件
const { VueLoaderPlugin } = require("vue-loader/dist/index");
// 指定HTML文件模板插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 复制文件夹插件 效果如:Vue的复制public
const CopyWebapckPlugin = require("copy-webpack-plugin");

module.exports = {
  mode: "development",
  // 设置 source-map,建立js映射文件,方便调试代码和错误
  devtool: "source-map",
  // 入口
  entry: "./src/index.js",
  // 出口
  output: {
    // 出口路径 必须是绝对路径
    // path.resolve()可以拼接出绝对路径
    // __dirname能拿到当前文件的路径信息(不含文件名称)
    path: path.resolve(__dirname, "./dist"),
    // 指定输出的文件名称
    filename: "bundle.js",
  },
  resolve: {
    // 别名
    alias: {
      // 对应的路径需要是绝对路径
      "@": path.resolve(__dirname, "./src"),
    },
    // 自动解析扩展名
    extensions: [".mjs", ".js", ".json", ".vue"],
    // 引入模块时的默认查找目录
    modules: ["node_modules"],
  },
  devServer: {
    // 如果希望其他地方可以访问,可以设置为 0.0.0.0
    host: "localhost", // 默认值 本质是一个域名,会被解析成 127.0.0.1
    // HMR 模块热替换(热更新),不刷新页面更改部分内容
    hot: true,
    port: 7070,
    // 自动打开浏览器 等价脚本:"serve":"webpack serve --open"
    open: true,
    // 开启 Gzip压缩
    compress: true,
    // 代理服务器
    proxy: {
      "/api": {
        target: "http://localhost:8888",
        // 重写路径
        pathRewrite: {
          "^/api": "",
        },
        // 跨域
        changeOrigin: true,
      },
    },
  },
  module: {
    // 配置loader,用于处理 css,js,图片,等等几乎所有的资源
    rules: [
      {
        //正则匹配指定的文件类型,\. 是因为 .在正则中具有特殊含义,需用 \ 转义
        test: /\.css$/,
        // 语法一:当只有一个loader时可以简写为:
        // loader:'css-loader'
        // 语法二:当不需要传递参数时多个loader可以简写:
        // use:['style-loader','css-loader']
        // 语法三:完整语法
        use: [
          {
            loader: "style-loader",
          },
          { loader: "css-loader" },
          {
            loader: "postcss-loader",
            // 给loader传递一些参数
            options: {
              // 传入 postcss-loader 配置
              postcssOptions: {
                plugins: [
                  // 插件
                  // require("autoprefixer")
                  // 预设(本身也是插件,集成在一起叫它预设)
                  require("postcss-preset-env"),
                ],
              },
            },
          },
        ],
      },
      // 这里需要新建一个对象,用来匹配 less文件,
      {
        test: /\.less$/,
        // less文件通过 less-loader 转化为css文件,
        // css文件通过 css-loader 正确解析后,交给 style-loader 插入到页面中
        use: ["style-loader", { loader: "css-loader" }, "less-loader"],
      },
      // less和 css 一起匹配
      // {
      //   test: /\.(less|css)$/,
      //   use: ["style-loader", "css-loader", "less-loader"],
      // },
      {
        test: /\.(png|jpe?g|gif|svg)$/,
        use: [
          {
            loader: "url-loader",
            options: {
              name: "img/[name]_[hash:6].[ext]",
              limit: 10 * 1024,
            },
          },
        ],
      },
      // webpack 5
      // {
      //   test: /\.(png|jpe?g|gif|svg)$/,
      //   type: "asset",
      //   generator: {
      //     filename: "img/[name]_[hash:6][ext]",
      //   },
      //   parser: {
      //     dataUrlCondition: {
      //       maxSize: 100 * 1024,
      //     },
      //   },
      // },
      {
        test: /\.(eot|ttf|woff2?)$/,
        use: {
          loader: "file-loader",
          options: {
            name: "font/[name]_[hash:6].[ext]",
          },
        },
      },
      // webpack 5
      // {
      //   test: /\.ttf|eot|woff2?$/i,
      //   type: "asset/resource",
      //   generator: {
      //     filename: "font/[name].[hash:6][ext]"
      //   }
      // }
      {
        test: /\.e?js$/i,
        use: {
          loader: "babel-loader",
          options: {
            presets: [["@babel/preset-env"]],
          },
        },
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new VueLoaderPlugin(),
    new HtmlWebpackPlugin({
      title: "webpack示例",
      // 指定模板
      template: "./public/index.html",
    }),
    new DefinePlugin({
      BASE_URL: '"./"',
    }),
    new CopyWebapckPlugin({
      // patterns 匹配
      patterns: [
        {
          // 从哪里复制
          from: "./public",
          // 复制到打包后的什么地方
          to: "public",
          globOptions: {
            // 需要忽略的文件
            ignore: ["**/index.html"],
          },
        },
      ],
    }),
  ],
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
最后更新于: 2021年9月15日星期三晚上10点10分
Faster Than Light
Andreas Waldetoft / Mia Stegmar