# Webpack5 预处理器 loader 与 插件 plugins

TIP

上一章节我们对 Webpack5 做了基础的入门学习,了解的 Webpack 的打包流程,同时学习了 Webpack5 两个核心的概念:entry 入口与 output 出口。

本章节我们接着学习另外两个核心的概念:loader 预处理和 plugin 插件。我们会从以下几个方面展开讲解

Webpack 预处理器 loader

  • 什么是 loader
  • 学习 babel-loader 预处理器

处理 css 的 loder

  • css-loader 与 style-loader
  • sass-loader

Webpack 插件 plugins

  • 什么是 plugins
  • clean-webpack-plugin 插件
  • html-webpack-plugin 插件
  • mini-css-extract-plugin 插件

Webpack 插件处理图片、音视频资源

  • file-loader 预处理器
  • url-loader 预处理器
  • Webpack 自带的 Asset Modules

解析 html 的 loader

  • html-withimg-loader 解析 html

CSS 样式兼容型处理

  • PostCSS 工具使用,postcss-loader
  • 安装 autoprefixer 插件

处理 JS 中的兼容问题

  • Babel 的配置文件
  • Babel 的预设包
  • Babel 官方的 polyfill
  • 利用 webpack 与 bable-loader 来实现 Babel 转码
  • 实现 Babel 按需转码

# 一、Webpack 预处理器 loader

TIP

本小节,我们会了解什么是 loader?同时借助 babel-loader 来学习如何在 webpack 中使用(配置)loader。

最后我们还会学习 css-loader 和 style-loader。

# 1、什么是 loader

TIP

loader 被翻译成中文为“加载器”,不过我们一般称之为预处理器,或直接称为他为 loader。

我们之前说 Webpack 是静态模块打包器,他可以处理 JS、CSS、图片等之类的文件,但其实 Webpack 本身只是用来处理 JS 和 JSON 文件。

如果要让 Webpack 能够去处理其它类型的文件,如:CSS、图片等些模块,就需要借助loader帮忙。

loader 的作用就是让 Webpack 能够去处理那些非 JS 和 JSON 文件的模块。

各种各样的 loader 可以让我们去处理各种不同类型的文件,你想处理什么类型的模块,你就加载对应的 loader 来处理就好。

接下来我们先借助balel-loader预处理器来学习 loader 是如何配置的,然后再学习另外两个重要的 loader,他们分别是:css-loaderstyle-loader

大家在学习时,重点关注如何在 Webpack 中配置相关的 loader,因为 Webpack 中的 loader 非常多,我们了解了如何配置 loader,以后需要用到的相关 loader,查看对应 loader 的文档说明就好。

不过上面提到的三个 loader 在我们实际开发中也常用,所以他们具体如何配置也是要掌握的。

# 2、babel-loader 预处理器

TIP

Webpack 他本身只是对 JS 做了打包,并没有处理他的兼容问题。也就是 Webpack 在打包过程中并没将 ES6 代码转成兼容低版本浏览器的 ES5 或 ES3 代码。

以下index.js文件

let username = "icoding";
const sex = "male";

const add = (x, y) => x + y;

new Promise((resolve, reject) => {
  resolve("成功 !");
}).then((value) => {
  console.log(value);
});

const obj = Object.assign({ a: 1 }, { b: 4 });
console.log(obj);

webpack.config.js文件配置内容如下:

const path = require("path");
module.exports = {
  mode: "none",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
};

利用 Webpack 打包后生成的bundle.js文件如下

/******/ (() => {
  // webpackBootstrap
  var __webpack_exports__ = {};
  let username = "icoding";
  const sex = "male";

  const add = (x, y) => x + y;

  new Promise((resolve, reject) => {
    resolve("成功 !");
  }).then((value) => {
    console.log(value);
  });

  const obj = Object.assign({ a: 1 }, { b: 4 });
  console.log(obj);
  /******/
})();

注:

如果需要在打包过程中对 JS 实现转码,就需要借助 Babel 来实现。先用 Babel 对 JS 代码转码,然后再将转码后的代码交给 WebPack 去打包。

不过 Babel 和 Webpack 是两个相互独立的工具,为了让两个相互独立的工具能互通起来,他们之间就需要有一个接口,这里的接口就叫着babel-loader。有了babel-loader,咱们的 Webpack 就可以与 Babel 互通了,也就是说可以在 Webpack 中使用 Babel 了。

接下来我们就在 Webpack 中尝试使用 Babel 来编译我们的代码。

# 2.1、使用 babel-loader

TIP

  • 在使用 bable-loader 前,需要先安装相应的 npm 包,即:babel-loader
  • 最终是使用 Babel 来实现转码,所以还需要安装 Babel 相关包,即:@babel/core
  • 转码过程中 Babel 如何知道把 ES6 的代码转换 ES5 还是 ES3 等 ?就需要选择对应的 Babel 预设来进行转码,所以还需要安装 @babel/preset-env 包。

使用以下命令一次性安装以上三个包

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

注:

我们是在 Webpack 中使用 Babel,所以并不需要安装@babel/cli包,但一定要记得先安装 Webpack 相关的包喽 !!

安装后,package.json 文件中devDependencies属性值如下

 "devDependencies": {
    "@babel/core": "^7.21.3",
    "@babel/preset-env": "^7.20.2",
    "babel-loader": "^9.1.2",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1"
  }

# 2.3、Webpack 中 babel-loader 的配置

TIP

接下来,我们在 Webpack 中如何使用 Babel 来实现转码,就需要借助 babel-loader,所以我们需要在 Webpack 的配置文件配置 babel-loader,告诉 Webpack。

webpack.config.js配置文件如下

const path = require("path");
module.exports = {
  mode: "none",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        // 只对 src 目录下的js文件进行处理
        include: /src/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env"],
          },
        },
      },
    ],
  },
};

以上配置文件解析

  • module:module 模块的意思,表示该配置项是用来对模块进行解析与处理。webpack 打包前,需要对模块做相关的处理,就会按 module 中的配置来。所有有关预处理器的配置,都是在配置项 module 下配置。
  • rules:是一个数组,数组中的每一项是一个 JS 对象,表示一类预处理器。比如:有专门处理 JS 的预处器 balbel-loader,也有专门处理 CSS 的预处理器(css-loader,style-loader)等。
  • test:是一个正则表达式或正则表达式数组,模块文件名与正则表达式相匹配的,会被 use 属性里的 loader 属性中对应的预处理器处理。上面/\.js$/表示匹配所有以.js 结尾的文件。
  • exclude:翻译成中文为“排除”的意思,排除指定目录下的模块。他的值可以是字符串或正则表达式,字符串必需是绝对地址。上面/node_modules/表示排除 node_modules 文件夹文件,不被 babel-loader 预处理器处理。
  • include:翻译成中文为“包含”的意思,表示只处理当前目录下与正则表达式匹配的文件。

如果 exclude 与 include 同时存在,Webpack 会优先使用 exclude 的配置。

  • use :表示要使用的预处理器,值可以是字符串、对象或数组。如果该处理器有些额外的配置参数,那 use 的需要设置为对象。如上面配置
  • loader:预处理器的名称
  • options:用来设置预处理器需要额外配置的参数

以上配置成功后,在当前目录npx webpack命令后,Webpack 就会在当前目录下寻找所有以.js结尾的文件,并排除node_module文件中的所有 JS 文件,然后把这些文件先交给bable-loader预处理器来处理,实现转码。最后将转码后的入口文件打包后输出。

index.js最后打包输出的bundle.js文件内容如下

/******/ (() => {
  // webpackBootstrap
  var __webpack_exports__ = {};
  var username = "icoding";
  var sex = "male";
  var add = function add(x, y) {
    return x + y;
  };
  new Promise(function (resolve, reject) {
    resolve("成功 !");
  }).then(function (value) {
    console.log(value);
  });
  var obj = Object.assign(
    {
      a: 1,
    },
    {
      b: 4,
    }
  );
  console.log(obj);
  /******/
})();

注:

打包后输出的的bundle.js成功实现了转码,不过里面的 API 还是不能转喽!关于 API 如何处理,后面再讲,这里主要讲 loader。

# 2.3、babel-loader 如何提速

TIP

在上面的webpack.config.js中,我们通过excludeinclude配置项,来确保转码时尽可能少的文件来提高转码的速度。

我们还可以通过使用cacheDirectory选项,设置cacheDirectory:true来开启缓存。在初始打包后再次打包,如果 JS 文件未发生变化,可以直接使用初次打包后的缓存文件,这样避免了二次转码,可以有效提高打包速度。

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      // 只对 src 目录下的js文件进行处理
      include: /src/,
      use: {
        loader: "babel-loader",
        options: {
          presets: ["@babel/preset-env"],
          // 开启缓存
          cacheDirectory: true,
        },
      },
    },
  ];
}

注:

babel-loader 缓存的文件默认保存在node_modules/.cache/babel-loader目录下

# 2.4、Babel 配置较复杂情况处理

TIP

在上面的配置中,Babel 相关的配置直接配置在webpack.config.js的 module-> rules -> use -> options 下面。如果 Babel 的配置较复杂的情况下,我们可以直接在当前工程目录下单独新建一个 Bable 的配置文件

如下:babel.config.json

以下是babel.config.json文件内容

{
  "presets": ["@babel/preset-env"]
}

Babel 的相关配置直接配置在了babel.config.json文件中,则webpack.config.js中对应的配置信息就可以删除了。删除后,webpack.config.js文件的内容如下:

const path = require("path");
module.exports = {
  mode: "none",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        // 只对 src 目录下的js文件进行处理
        include: /src/,
        use: {
          loader: "babel-loader",
          options: {
            // 不能写在babel.config.json,会抛出错误
            cacheDirectory: true,
          },
        },
      },
    ],
  },
};

# 2.5、总结 loader 及使用流程

TIP

默认 Webpack 只能处理 JS 和 JSON 文件,对于其它类型的文件需要借除 loader 预处理器来帮忙。

loader 的作用就是让 Webpack 能够去处理那些非 JS 和 JSON 文件的模块。

loader 的使用流程

  • 安装 loader 需要的相关包
  • webpack.config.js文件的module选项中配置loader,具体格式如下
module.exports = {
  // loader相关模块在module选项中中
  module: {
    // rules的值是一个数组,数组中的每一项是一个JS对象,表示一类预处理器
    rules: [
      // 第一个预处理器相关配置
      {
        // 是一个正则表达式或正则表达式数组,模块文件名与正则表达式相匹配的,会被use属性里的loader属性中对应的预处理器处理
        test: /正则/,
        // 排除该文件夹的文件,不需要处理
        exclude: /文件夹名或目录/,
        // 只处理该文件夹下符合条件的文件
        include: /文件夹名或目录/,
        // 表示要使用的预处理器,值可以是字符串、对象或数组
        use: {
          // loader的名称
          loader: "loader名称",
          // 预处理器需要额外配置的参数
          options: {
            //
          },
        },
      },
      {
        // 第二个预处理器相关配置
      },
    ],
  },
};

# 二、处理 CSS 的 loader

TIP

以下会讲解 css-loader、style-loader、sass-loader 来处理 CSS 文件

# 1、css-loader 与 style-loader 预处理器

TIP

前面我们说过,Webpack 本身只能处理 JS 和 JSON 类型的文件,对于 CSS 类型的文件自然是处理不了的。

如果我们在 JS 中使用import("./basic.css")函数来加载 CSS 文件,默认情况下 Webpack 打包会失败,在命令行抛出错误。

而 css-loader 就是用来解析 css 文件,同时会将解析后的 CSS 文件以字符串的形式打包到 JS 文件中。不过,此时的 CSS 样式并不会生效,因为需要把 CSS 文件插入到 HTML 文件中才会生效。

所以我们还需要借助style-loader来帮忙,他可以把 JS 里面的样式代码插入到 HTML 文件中。他的原理很简单,就是动态生成 style 标签,然后将 CSS 内容添加到 style 标签中,然后将 style 标签插入到 HTML 页面。

代码演示

// 项目目录结构

icoding
├─ node_modules
├─ package-lock.json
├─ package.json
├─ src
│  ├─ basic.css
│  └─ index.js
└─ webpack.config.js

//  index.js文件中内容
import "./basic.css";
console.log('index.js')

// webpack.config.js文件内容如下
const path=require("path");
module.exports={
    mode:"none",
    entry:"./src/index.js",
    output:{
        path:path.resolve(__dirname,"dist"),
        filename:"index.js"
    }
}
/*basic.css 文件内容*/
body {
  background-color: red;
}

注:

以上配置好了后,在当前目录下执行npx webpack执行打包命令时,控制台报错:

You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.

上面英文的意思是:你需要一个合适的 loader 来处理这类文件,目前没有配置任何加载器来处理该文件

# 1.1、 安装 css-loader 与 style-loader

执行以下命令,安装 css-loader 和 style-loader 的包

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

安装成功后,package.json文件devDependencies字段的信息如下:

"devDependencies": {
    "css-loader": "^6.7.3",
    "style-loader": "^3.3.2",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1"
  }

# 1.2、Webpack 中配置 css-loader 与 style-loader

以下是配置好的webpack.config.js文件

const path = require("path");
module.exports = {
  mode: "none",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        // 表示将匹配到的css文件先交给css-loader处理,然后
        // 将处理后的内容再交给style-loader来处理。
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};

最后在当前目录执行npx webpack命令打包,打包后的目录结构如下:

icoding
├─ node_modules
├─ dist
│  ├─ 1.bundle.js  // basic.css单独打包后生成的js文件
│  └─ bundle.js  // index.js打包后的出口文件
├─ index.html
├─ package-lock.json
├─ package.json
├─ src
│  ├─ basic.css
│  └─ index.js
└─ webpack.config.js

我们把在index.html页面中引入bundle.js。最后在浏览器中浏览index.html页面,页面的背景变成了红色。在当前面右击---检查,在 Elements 选项中可以看到生成的 style 标签

image-20230324011051576

# 2、css-loader 对 css 中图片的处理

TIP

css-loader在处理 CSS 样式时,会处理 CSS 中通过url()方式引入的资源。

接下来我们通过代码来演示下

新建以下目录结构

icoding
├─ node_modules
├─ package-lock.json
├─ package.json
├─ src
│  ├─ css
│  │  └─ basic.css
│  ├─ images
│  │  ├─ 18.mp4
│  │  └─ course.jpg
│  └─ js
│     └─ index.js
└─ webpack.config.js

我们把basic.css文件的内容更新为如下:

/*basic.css 文件内容*/
body {
  background-color: red;
  background: url("../images/course.jpg");
  /* background: url("../images/18.mp4"); */
}
  • index.js文件内容如下
import "../css/basic.css";
console.log("index.js");
  • package.json 内容如下
{
  "name": "icoding",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^6.7.3",
    "style-loader": "^3.3.2",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1"
  }
}

接下来,我们更改配置文件如下:

const path = require("path");
module.exports = {
  mode: "none",
  entry: "./src/js/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "./js/bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        // use: ["style-loader", "css-loader"],
        // 或写成下面这样
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              url: true, // true表示图片url方式引入的资源,false表示不处理
            },
          },
        ],
        //
      },
    ],
  },
};

执行npx webpack打包后,生成的目录结构变为:

icoding
├─ node_modules
├─ dist
│  ├─ 2715c2f1ee2c4063d887.jpg
│  └─ js
│     └─ bundle.js
├─ package-lock.json
├─ package.json
├─ src
│  ├─ css
│  │  └─ basic.css
│  ├─ images
│  │  ├─ 18.mp4
│  │  └─ course.jpg
│  └─ js
│     └─ index.js
└─ webpack.config.js

注:

观察以上目录,我们发现图片被成功处理并复制到了当前dist的根目录下。

但我们在实际开发中不会用css-loader来处理 css 中的图片,而是选择后面的file-loaderurl-loader来处理。因为 file-loader 可以处理导入的各种类型的文件,而 css-loader 只能处理 css 中的图片等资源,并且在后期与file-loader在处理图片时发生冲突。

以上我们安装的css-loader@6.7.3 版本,也就是 css-loader 的第 6 个版本才有处理图片等资源的能力,对于 6 以下的版本是没有处理图片功能的。如:css-loader@5.0.2`版本

# 3、sass-loader 预处理器

TIP

在实际的开发中,我们通常用会用 Sass 或 less 来书写样式文件,但是浏览器本身是不认识 Sass 的,我们还是需要将 Sass 转换成最终浏览器能够识别的 CSS 文件。这就需要用到 sass-loader 来帮我们处理。

Sass 的官网:https://www.sass.hk/ (opens new window)

以下是一段 Sass 书写的样式

.box {
  display: flex;
  width: 100px;
  height: 100px;
  p {
    background-color: #ddd;
    span {
      color: red;
    }
  }
}

上面 Sass 转换成 CSS 如下

.box {
  display: flex;
  width: 100px;
  height: 100px;
}

.box p {
  background-color: #ddd;
}
.box p span {
  color: red;
}

我们后面讲 Vue 项目时就会采用 Sass 来书写样式,所以我们主要介绍 Sass 如何处理。

创建基础的项目结构

icoding
├─ node_modules
├─ index.html
├─ package-lock.json
├─ package.json
└─ src
   ├─ css
   │  └─ basic.scss
   └─ index.js
  • basic.scss 文件内容如下
.box {
  display: flex;
  width: 100px;
  height: 100px;
  p {
    background-color: #ddd;
    span {
      color: red;
    }
  }
}
  • index.js 内容如下
import "./css/basic.scss";
  • index.html 页面内容如下
<script src="./dist/bundle.js"></script>
<body>
  <div class="box">
    <p>成功编译了<span>scss</span>文件</p>
  </div>
</body>

# 3.1 、sass-loader 的使用

TIP

首先我们需要安装sass-loader所需要的 npm 包,sass-loader 底层依赖 Node Sass 或 Dart Sass 进行处理,它们对应的 npm 包分别为 node-sass 和 sass。 这里推荐安装 sass 包

执行以下命令,安装 sass 包

npm install sass-loader sass  --save-dev

sass 转成 css 后,还需要用 css-loader 和 style-loader 来处理,所以也需要安装

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

安装成功后package.json文件中devDependencies字段信息如下

"devDependencies": {
    "css-loader": "^6.7.3",
    "style-loader": "^3.3.2",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1"
  }

接下来配置webpack.config.js文件,内容如下

const path = require("path");
module.exports = {
  mode: "none",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "./bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.(s[ac]ss|css)$/i,
        // 从右往左调用对应的loader来处理,先用sass-loader将sass处理成css,再将用css-loader来处理css,最后用style-loader将css插入到style标签中,并添加到html页面
        use: ["style-loader", "css-loader", "sass-loader"],
      },
    ],
  },
};

最后执行npx webpack打包,打包后在浏览器打开index.html页面,效果如下

image-20230325011928188

sass 被成功的处理成了 css,在配合 css-loader 与 style-loader 最终添加到了 html 页面

# 三、Webpack 插件 plugins

TIP

本小节,我们会了解什么是 plugins 插件 ?同时借助 clean-webpack-plugin 插件来学习如何在 webpack 中使用插件。

最后我们还会学习 html-webpack-plugin 插件和 mini-css-extract-plugin 插件。

# 1、什么是 plugins

TIP

plugins 翻译过来就是 “插件” 的意思,我们很难去描述 Webpack 插件他是干什么的。如果站在更底的角度来理解,Webpack 插件是在 Webpack 编译的某些阶段,通过调用 Webpack 对外暴露的 API 来扩展 Webpack 的能力。

对于上面这句话,暂时不理解没关系,当我们学到如何开发 Webpack 插件时,你自然就能理解上面这句话了。

在这里,我们只要把他和 loader 做区分就好。loader 是用来解析模块的,让 Webpack 可以去处理非 JS 和 JSON 类型的其它文件模块。而插件可以让 Webpack 执行范围更广的任务。

Webpack 的插件非常多,我们可以具体的插件具体来分析。那 Webpack 具体有那些插件,分别有什么用?

大家可以参考官方提供的:https://webpack.docschina.org/plugins/ (opens new window) 当然还有很多官网没有提供的,大家可以通过搜索引擎自行搜索。

接下来我们选择 3 款插件,来帮助大家学习如何在 Webpack 使用插件,本质上就是学习如何在 Webpack 中配置我们的插件。掌握了方法后,具体的插件如何配置,大家只需要根据对应插件的说明文档来就好。所以再多的插件,大家也不用担心不会用的问题。

# 2、clean-webpack-plugin 插件

TIP

clean-webpack-plugin插件是一个清除文件插件。每次使用 Webpack 打包后,都会在output.path指定的目录下生成很多打包后的出口文件(资源),当我们再次打包的时候,我们就需要手动的先将原来的打包后的文件给清空。

clean-webpack-plugin插件就是帮我们做这件事的,有了他,我们每次打包后,会在生成新的出口文件(资源前),先将原来的文件给删除掉。

# 2.1、clean-webpack-plugin 插件的安装

在使用clean-webpack-plugin插件前,我们需要先通过以下命令,安装插件所需要的包

npm install --save-dev clean-webpack-plugin

安装后package.json文件中devDependencies字段内容如下

"devDependencies": {
    "clean-webpack-plugin": "^4.0.0",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1"
  }

# 2.2、clean-webpack-plugin 插件的使用

TIP

所谓的使用插件,就是如何在 Webpack 中来配置插件信息。所有 Webpack 需要用到的插件都需要配置在webpack.config.js文件的plugins选项中。

当前项目的webpack.config.js文件中的配置信息,如下

const path = require("path");
// 在使用插件前,需要先导入插件模块
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name]_[chunkhash:8].js",
  },
  // 配置插件,目前只使用了一个插件
  plugins: [new CleanWebpackPlugin()],
};

配置说明

  • 在使用插件前,先需要导入对应的插件,以下表示加载对应的插件模块
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
  • 接下来我们需要告诉 Webpack,使用那些插件来扩展对应功能。将需要使用的插件在plugins选项中来配置。
  • plugins 的值是一个数组,数组中的每个成员表示一个插件,这里的成员都是通过 new 关键字创建的实例对象。
  • 如果插件有相关的参数,则直接将一个对象作为插件的构造函数的参数传入就好。上面没有传入对应的参数,则表示启用默认配置。该插件默认使用output.path目录作为需要清空的目录。

clean-webpack-plugin插件更多配置,查询文档 (opens new window)

# 2.3、执行 Webpack 打包

TIP

完成以上配置后,我们在当前工作目录下执行npx webpack打包

当前项目目录结构

icoding
├─ node_modules
├─ package-lock.json
├─ package.json
├─ src
│  └─ index.js
└─ webpack.config.js

打包后目录结构如下

icoding
├─ node_modules
├─ dist
│  └─ main_628c4b78.js    // 创建dist目录,并在该目录下保存了打包后的出口文件
├─ package-lock.json
├─ package.json
├─ src
│  └─ index.js
└─ webpack.config.js

接下来我们更改index.js文件中的内容,重新执行npx webpack打包,打包后目录结构如下:

icoding
├─ node_modules
├─ dist  // 上一次打包后的出口文件被删除了,只留下这一次打包生成的出口文件
│  └─ main_36eef7b8.js
├─ package-lock.json
├─ package.json
├─ src
│  └─ index.js
└─ webpack.config.js

如果不使用clean-webpack-plugin插件(即删除webpack.config.js中的 plugins 选项),然后重新修改index.js中的内容,执行npx webpack打包,打包后目录结构如下

icoding
├─ node_modules
├─ dist   // 上一次打包的出口文件还在,并没有删除
│  ├─ main_36eef7b8.js
│  └─ main_59174d32.js
├─ package-lock.json
├─ package.json
├─ src
│  └─ index.js
└─ webpack.config.js

# 3、html-webpack-plugin 插件

TIP

html-webpack-plugin插件可以自动创建 HTML 文件,并且会自动将打包后的 JS、CSS 等资源引入到创建的 HTML 文件中。当然,也可以使用指定的 HTML 模板,因为自动创建的 HTML 模块功能有限。

在这之前,我们需要手动将打包后的 JS、CSS 等文件引入到 HTML 页面中来使用。

接下来,我们用代码来演示下

  • 项目目录结构
icoding
├─ node_modules
├─ index.html  // 访问的页面
├─ package-lock.json
├─ package.json
├─ src
│  └─ index.js
└─ webpack.config.js
  • webpack.config.js配置文件
const path = require("path");
module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name]_[chunkhash:8].js",
  },
};
  • 打包后生成的目录结构
icoding
├─ node_modules
├─ dist
│  └─ main_36eef7b8.js  // 打包后生成的出口文件
├─ index.html  // index.html页面需要引用上面的出口文件
├─ package-lock.json
├─ package.json
├─ src
│  └─ index.js
└─ webpack.config.js
  • 接下来我们需要在index.html页面使用刚才打包好的出口 JS 文件,我们只能手动在index.html页面来引用,如下:
<body>
  网站首页
  <script src="./dist/main_36eef7b8.js"></script>
</body>

注:

因为打包时,出口文件的名字采用的是 hash 值,所以每次文件内容有变化,重新打包后,文件的名称就不一样,我们就要手动更新 HTML 页面对打包后文件的引用。

这种重复没有价值的工作,确实让人很烦躁。有那html-webpack-plugin插件后,我们就可以从中解放出来,因为他就是帮我们做这件事的。

# 3.1、html-webpack-plugin 插件的安装

TIP

接下来我们就在上面的项目基础上,来使用html-webpack-plugin插件

在使用html-webpack-plugin插件前,我们需要使用以下命令来安装插件对应的包

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

安装后package.json文件中devDependencies字段内容如下

"devDependencies": {
    "clean-webpack-plugin": "^4.0.0",
    "html-webpack-plugin": "^5.5.0",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1"
}

当前项目中还使用了前面讲到的 clean-webpack-plugin插件

# 3.2、html-webpack-plugin 插件的使用

TIP

插件安装成功后,接下来需要在webpack.config.js文件的plugins选项中添加相关配置来使用该插件。

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
// 导入插件模块
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name]_[chunkhash:8].js",
  },
  plugins: [
    // 清除文件插件
    new CleanWebpackPlugin(),
    // 自动创建一个空的HTML文件,并在页面中引用打包后的JS文件
    new HtmlWebpackPlugin(),
  ],
};

配置说明

  • 以下代码为,导入html-webpack-plugin插件对应的模块
const HtmlWebpackPlugin = require("html-webpack-plugin");

在 plugins 选项中,配置插件,告诉 Webpack 可以使用该插件来完成相关功能

plugins: [
  // 该插件会自动创建一个空的HTML文件,并在页面中引用打包后的JS文件
  new HtmlWebpackPlugin(),
];

# 3.3、执行 Webpack 打包

TIP

按以上配置好之后,接下来在当前目录下执行npx webpack 完成打包

打包后的目录结构如下

icoding
├─ node_modules
├─ dist  // 在dist目录下生成了html和js文件
│  ├─ index.html
│  └─ main_7b90bcf6.js
├─ index.html
├─ package-lock.json
├─ package.json
├─ src
│  └─ index.js
└─ webpack.config.js

打包后,dist 目录下的index.html是插件自动帮我们创建的,页面代码如下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Webpack App</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <script defer src="main_7b90bcf6.js"></script>
  </head>
  <body></body>
</html>

注:

观察以上代码,我们可以看到,html 页面中自动插入了我们打包后生成的main_7b90bcf6.js

# 3.4、自定义模板

TIP

在实际开发中,我们希望能以指定的 html 页面来做为模板,生成新的 html 页面。也就是指定插件以某个 html 文件为模板,生成新的 html 文件,并把打包后的 JS 文件添加到该 html 页面中。

要实现以上功能,我们只需要在webpack.config.js文件中,做相关的配置就好,修改webpack.config.js中 plugins 选项内容如下:

 plugins:[
        // 清除文件插件
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
        	// 指定生成的html文件以当前目录下的index.html文件为模板
            template:"./index.html",
        })
    ]

当前目录下的index.html文件内容如下

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    我是根目录下的index.html文件
  </body>
</html>

打包生成后,dist目录下生成了 index.html 和main_7b90bcf6.js文件,其中 index.html 文件的内容如下

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script defer src="main_7b90bcf6.js"></script>
  </head>
  <body>
    我是根目录下的index.html文件
  </body>
</html>

生成后的 html 文件是以根目录下的index.html为模板生成的,同时还把打包后的 JS 文件自动添加到了index.html页面中。

# 3.5、html-webpack-plugin 的参数

TIP

html-webpack-plugin插件,不仅支持单入口打包,也支持多入口打包。可以同时生成多个 html 文件,并把对应的 JS、CSS 等资源引用到指定的 html 文件中。

案例演示

我们根据以下目录结构来创建对应的文件

icoding
├─ node_modules
├─ package-lock.json
├─ package.json
├─ src
│  ├─ index.html
│  ├─ index.js
│  ├─ search.html
│  └─ search.js
└─ webpack.config.js
  • index.html 内容如下
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    网站首页
  </body>
</html>
  • search.html 内容如下
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    网站搜索页
  </body>
</html>
  • package.json 内容如下
{
  "name": "icoding",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "clean-webpack-plugin": "^4.0.0",
    "html-webpack-plugin": "^5.5.0",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1"
  }
}
  • 当前项目的webpack.config.js文件内容如下
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  mode: "none",
  entry: {
    index: "./src/index.js",
    search: "./src/search.js",
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name]_[chunkhash:8].js",
  },
  plugins: [
    // 清除文件插件
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: "./src/index.html",
      filename: "index.html",
      chunks: ["index"],
      minify: {
        // 删除index.html中的空格
        collapseWhitespace: true,
        // 删除index.html中的注释
        removeComments: true,
      },
    }),
    new HtmlWebpackPlugin({
      template: "./src/search.html",
      filename: "search.html",
      chunks: ["search"],
    }),
  ],
};

new HtmlWebpackPlugin()插件的参数说明

  • template:指定生成的 HTML 的模块文件,最终以这里指定的文件为模板,生成最终的 HTML 文件
  • filename:最终生成的 HTML 文件的名称
  • chunks:指定当前 HTML 文件引入那个入口文件打包后生成的出口文件
  • minify:用来指定打包后文件是否做相关体积压缩的处理。如果值为 true,则按默认配置压缩 HTML 代码,也可以向上面一样,设置为一个对象,指定相关的压缩处理。

更多参数,查阅文档 (opens new window)

  • 打包后生成的目录结构
icoding
├─ node_modules
├─ dist
│  ├─ index.html
│  ├─ index_433c545f.js
│  ├─ search.html
│  └─ search_26a2e76a.js
├─ package-lock.json
├─ package.json
├─ src
│  ├─ index.html
│  ├─ index.js
│  ├─ search.html
│  └─ search.js
└─ webpack.config.js

生成后index.html的内容如下

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>Document</title>
    <script defer="defer" src="index_433c545f.js"></script>
  </head>
  <body>
    网站首页
  </body>
</html>

打包后生成的search.html的内容如下

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script defer src="search_26a2e76a.js"></script>
  </head>
  <body>
    网站搜索页
  </body>
</html>

# 4、mini-css-extract-plugin 插件

TIP

在前面学习css-loaderstyle-loader时,我们并不能将 CSS 内容单独提取到一个 CSS 文件中,而mini-css-extract-plugin插件就是用来作这件事的。他会将 CSS 提取到单独的 CSS 文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件。

所以他一般需要与css-loader一起配合使用。css-loader负责解析 CSS 模块,会将解析后的 CSS 文件以字符串的形式打包到 JS 文件中,而mini-css-extract-plugin负责将 JS 中的 CSS 提取到单独的 CSS 文件中。

本插件基于 webpack v5 的新特性构建,并且需要 webpack 5 才能正常工作

演示案例

接下来我们创建项目来演示下,以下是项目的目录结构

// 项目目录结构
icoding
├─ node_modules
├─ index.html
├─ package-lock.json
├─ package.json
├─ src
│  ├─ basic.css
│  └─ index.js
└─ webpack.config.js
  • index.html 页面代码
<!--省略了部分,只留下了重要信息-->
<body>
  <div id="box"></div>
</body>
  • basic.css 文件内容
body {
  background-color: red;
}
  • index.js 文件内容
import("./basic.css");
console.log("index.js");
  • 安装 webpack、clean-webpack-plugin、html-webpack-plugin 插件
npm i webpack webpack-cli clean-webpack-plugin  html-webpack-plugin -D

# 4.1 安装 mini-css-extract-plugin 和 css-loader 插件

项目创建好后,接下来执行以下命令安装mini-css-extract-plugincss-loader插件对应的包

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

安装后package.jsondevDependencies的信息如下

"devDependencies": {
    "clean-webpack-plugin": "^4.0.0",
    "css-loader": "^6.7.3",
    "html-webpack-plugin": "^5.5.0",
    "mini-css-extract-plugin": "^2.7.5",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1"
  }

# 4.2、插件配置

TIP

安装好对应插件后,接下来就可以通过修改 Webpack 配置,来使用这个两个插件

webpack.config.js的内容如下

const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
  mode: "none",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
    chunkFilename: "[name]_[chunkhash:8].js",
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "basic_[contenthash:8].css",
      // 找到 id 为 box 的元素,在它之后插入新的 <link> 元素
      insert: "#box",
      // 把指定的属性和值附加到 <link> 元素上
      attributes: {
        id: "target",
        "data-target": "example",
      },
    }),
    new HtmlWebpackPlugin({
      template: "./index.html",
    }),
    new CleanWebpackPlugin(),
  ],
};

MiniCssExtractPlugin 插件的配置说明

  • filename: 抽离出来的 CSS 文件名
  • insert: 值是一个 css 选择器字符串,用来指定生成的 CSS 的<link>标签插入的位置,<link>标签会插入到此选择器选中的元素后面
  • attributes:值是一个对象,指定相关属性和值,添加到<link> 标签上

更多配置,查阅官方文档 (opens new window)

# 4.3、执行 Webpack 打包

TIP

在当前目录下执行npx webpac执行打包,打包后,生成的目录结构如下

image-20230324023023318

观察以上图,我们可以看到index.js中通过import("./basic.css")加载的 CSS 样式,打包后单独生成了1.basic_4a73e62e文件。

接下来,在浏览器中预览 dist 目录下的 index.html 文件,发现页面背景变红,然后右击 -> 审查,查看Elements面板,如下图:

image-20230324023123521

注:

观察以上图,我们可以看到 css 被当作单独的文件,通过 link 标签引入到 html 页面中。

同时<link>标签被插入到"body .box"元素后面。还有<link>标签上添加了 id 与 data-target 属性

# 四、Webpack 处理图片、音视频资源

TIP

本小节我们来学习使用 file-loader 来处理 JS 中加载的图片视音频等资源。

# 1、file-loader 预处理器

TIP

file-loader 是一个文件资源预处理器,他在 Webpack 中的作用是:处理文件导入语句并替换成它的访问地址,同时把文件输出到相应位置。

其中导入语句包括 JS 的import ...from "..." 和 CSS 样式中的url()

# 1.1 file-loader 处理 JS 中引入的图片

TIP

接下来我们要在load-img.js文件中通过import ... from "..."语句来导入一张图片,然后把这张图片插入到打包后的index.html页面中的#box元素中。

项目的相关信息如下:

icoding
├─ node_modules
├─ package-lock.json
├─ package.json
├─ src
│  ├─ images
│  │  └─ logo.png
│  ├─ index.html
│  └─ js
│     └─ load-img.js
└─ webpack.config.js

package.json中``字段信息如下

 "devDependencies": {
    "clean-webpack-plugin": "^4.0.0",
    "html-webpack-plugin": "^5.5.0",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1"
  }
  • index.html文件内容如下
<body>
  <div id="box"></div>
</body>
  • load-img.js文件内容如下
import img from "../images/logo.png";
let imgEl = new Image();
imgEl.src = img;
// 页面加载完成,再将图片插入页面中
window.onload = function () {
  document.getElementById("box").appendChild(imgEl);
};

安装 file-loader

执行以下命令,安装 file-loader 所需要的包,注意版本号

npm install file-loader@6.2.0 --save-dev

配置 webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
  mode: "none",
  entry: "./src/js/load-img.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/bundle.js",
    chunkFilename: "./js/[name].js",
  },
  module: {
    rules: [
      {
        /* 被file-loader处理的文件后缀名 */ test: /\.(png|jpe?g|gif)$/i,
        use: {
          /* loader的名称 */
          loader: "file-loader",
          /* loader的参数设置 */
          options: {
            /* 打包后生成的图片名称,[ext] 图片后缀名与原来一样*/
            name: "[name].[contenthash:8].[ext]",
            /* 打包后生成的图片输出目录,相对于output.path而言*/
            outputPath: "./images/",
            /* 
                        publicPath默认以output.publicPath作为访问资源的地址
                        图片在网页中的预览地址,相对于被调用者而言*/
            publicPath: "./images/",
          },
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
    new CleanWebpackPlugin(),
  ],
};

以上配置好后,在当前目下执行npx webpack打包,打包后生成的目录结构如下

icoding
├─ node_modules
├─ dist
│  ├─ images
│  │  └─ logo.ad0f1f9b.png
│  ├─ index.html
│  └─ js
│     └─ bundle.js
├─ package-lock.json
├─ package.json
├─ src
│  ├─ images
│  │  └─ logo.png
│  ├─ index.html
│  └─ js
│     └─ load-img.js
└─ webpack.config.js

注:

观察以上结构,打包后的图片保存在 dist/images 目录下,js 保存在 dist/js 目录下,index.html页面在 dist 目录下。

接下来在浏览器中访问index.html页面,发现图片被成功插入到页面中。如下截图

image-20230324212709685

image-20230324212737602

# 1.2、file-loader 处理 CSS 中的图片

TIP

我们在上面案例的基础上

  • 在 images 文件夹下新加了一张图片
  • src/js目录下添加index.js
  • src/css目录下添加basic.css文件
  • 然后在index.js文件中引入basic.css样式文件

index.js文件内容

import "../css/basic.css";
  • basic.css内容
body {
  background: url("../images/course.jpg");
  /* background: url("../images/18.mp4"); */
}

  • 当前项目目录结构如下
icoding
├─ node_modules
├─ package-lock.json
├─ package.json
├─ src
│  ├─ css
│  │  └─ basic.css  // 新增
│  ├─ images
│  │  ├─ 18.mp4  // 新增
│  │  ├─ course.jpg  // 新增
│  │  └─ logo.png
│  ├─ index.html
│  └─ js
│     ├─ index.js  // 新增
│     └─ load-img.js
└─ webpack.config.js

安装包

执行以下命令安装处理 css 需要的包

npm i css-loader@5.2.7 mini-css-extract-plugin@2.7.5 -D

注意我们安装的 css-loader 是 5 版本的,而不是 6 版本的

修改webpack.config.js配置文件,如下

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
  mode: "none",
  entry: {
    img: "./src/js/load-img.js",
    index: "./src/js/index.js",
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/[name][chunkhash:8].js",
    chunkFilename: "./js/[name].js",
  },
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|mp4)$/i,
        use: {
          loader: "file-loader",
          options: {
            name: "[name].[contenthash:8].[ext]",
            outputPath: "./images/",
            // publicPath:'./images/'
          },
        },
      },
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: "./css/basic_[contenthash:8].css",
    }),
  ],
};

最后在当前目录执行npx webpack命令打包后,生成的目录结构如下

icoding
├─ node_modules
├─ dist
│  ├─ css
│  │  └─ basic_f258e178.css
│  ├─ images
│  │  ├─ course.2715c2f1.jpg
│  │  └─ logo.ad0f1f9b.png
│  ├─ index.html
│  └─ js
│     ├─ img6db9452f.js
│     └─ index32ec9c4b.js
├─ package-lock.json
├─ package.json
├─ src
│  ├─ css
│  │  └─ basic.css
│  ├─ images
│  │  ├─ 18.mp4
│  │  ├─ course.jpg
│  │  └─ logo.png
│  ├─ index.html
│  └─ js
│     ├─ index.js
│     └─ load-img.js
└─ webpack.config.js

注:

观察以上图,我们发现按我们预期的完成了打包,然后在浏览器中打开index.html页面,可以正常访问到两张图片和对应的 css 和 js 文件。

温馨提示

当我们同时用到 css-loader 和 file-loader 时,一定要安装 css-loader 的第 5 个版本,因为第 6 个版本也能处理 url 中的图片等资源,造成与 file-loader 冲突,而打包失败。

# 1.3、file-loader 总结

TIP

file-loader 不仅可以处理图片,还可以处理视频、音频等媒体资源。

它的实现原理就是在打包时根据资源的地址,把这些资源复制一份到打包后的指定目录,然后重新取个名字,并替换资源的访问路径。

# 2、url-loader 预处理器

TIP

url-loader可以看作是file-loader的增强版,他除了支持file-loader的所有功能外,还可以将图片转换成 Base64 编码格式的 DataURL,并将其作为模块来引入到打包后的代码中,以减少 HTTP 请求的数量。

url-loader 可以通过配置相关的参数,限定小于多少 KB 的图片才处理成 Base64 编码格式的 DataURL,大于这个限制的按 file-loader 的方式来处理。

正常情况下,html 中引入图片的代码如下:

<img src="./images/go.png" />

如果将 go.png 转换成 Base64 编码格式,html 中引入的图片代码如下:

<img
  src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAq5JREFUWEfV10toE1EUBuD/3NQ0k5Ra6wMFcaUbZ1oFH1UQ7EbaZFKkYBFxoaBr8YErF4qIqyKiG9GFFV1I40JoMtSVIihVUWsy8YEKBQtFQWt9ZGLazJFJSWuTNDPVaYJZJcy593xzc+bcO4Qqf6jK+VEEMPqUVgsldej3KoGbAcglF7gL8JDh9a1v3PF0bL4RMwA/+uR1HkGDVlISpPqCCa2iACtZKqZ8JGAZCGelkH6i4gAjqtwAYS+A+5Kqb68CQN4Hoh4A2XSmZsmizsGv84koegrS/fJqztJbK6lJ1BkIJW5XFGAlM6JNOohlIjrvCyWOVB6gyQNgagF4xDTpspsAUTPRIwVfD+XnLNkJ05rSzYxjbiaemovxTgrra8oCUnfWttCEGJgXAPBBUvVVZQHWxXRMGWWgwXUE4aYU0vfYAzQ5wky73AYw45A/rF+0BaSiTV1E3Os2wOMRG7zt8Wf2AK15JbGZBFDvFoKBUb+qN/45X9nzgBFTrC3ZxXZM/ZKaCDoGpGLyOQK514iYT0vh5EnngNnqwOOz/1ey6aKYLDhYpyb7nQMm6+A9AG/ujCCtgHdbBLRgoS2Ax78h82A3ODWcj/3lmxhfSjvffHcMyO0LmvIQjK3Wd9G4Ed4tV22T5wMyjw7A/Pw4/7Pk9m57KC2sA9HQDNQutkdkvsAcfTHdgRnd/rB+vHCgPcClfiBIdNWG4rfmDLAGpKLKBRJosr/t0hHM/NyvJo+Wumq7An+b1Om4/wfwqVeuqw/QdabpHZIZLyVP9gy1vxopvON0VG5jwiXTpGuBDv3UbCvieAWMmLwfoKJn0BS8ORBMPilMYMTkgwBdAUOTwrr6z4DJYpQPM9PUGUEIHhqr8USWt8V/FgH6lFYW2CQEJcu94DheAadFNde4qgN+A4UD5iFU5kdpAAAAAElFTkSuQmCC"
/>

# 2.1、url-loader 的安装与使用

TIP

我们在上面file-loader案例的基础上来操作。file-loader 案例中logo.png图片大小为9.8KB,而courser.jpg图片大小为20KB

因为url-loader本身需要依赖file-loader的,所以我们在安装 url-loader 时,也需要同时安装 file-loader。

执行以下命令,安装 url-loader 包(file-loader 上面案例中安装过)

npm install url-loader@4.1.1 --save-dev

安装后,我们修改webpack.config.js配置文件如下

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
  mode: "none",
  entry: {
    img: "./src/js/load-img.js",
    index: "./src/js/index.js",
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/[name][chunkhash:8].js",
    chunkFilename: "./js/[name].js",
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
      {
        // 根据正则表达式来匹配要处理的文件类型
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          // 处理的loader名
          loader: "url-loader",
          // 参数设置
          options: {
            // 当图片的大小小于10kb时,会将其转成Base64编码格式的DataURL
            limit: 10240, // 10240=1024*10 相当于10k大小
            name: "[name].[ext]", // 图片的名字
            // 图片的输出目录,相对于output.path而言
            outputPath: "./mages/",
            // 图片的预览地址,相对于被调用者与ouput.publicPath机制一样
            publicPath: "/dist/images/",
          },
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({
      filename: "./css/basic_[contenthash:8].css",
    }),
  ],
};

以上配置好后,在当前目录执行npx webpack打包,生成如果的dist目录结构如下

dist
  ├─ css
  │  └─ basic_1beaad77.css
  ├─ images
  │  └─ course.jpg
  ├─ index.html
  └─ js
     ├─ img1509d649.js
     └─ index59c407b4.js

观察上面结构,我们发现只有course.jpg被成功复制到了images文件夹中,logo.png没有出现。接下来我们在浏览器中访问index.html页面,查看源代码,如下

image-20230325000235409

因为logo.png的小于10KB,所以被成功处理成Base64位的。

# 3、html-withimg-loader 预处理器

TIP

html 中直接使用 img 标签 src 加载图片的话,因为没有被依赖,图片将不会被打包。

html-withimg-loader预处理器,主要就是用来处理 html 文件中的通过 img 标签引入的图片。 不过他主要是用来解释 html 文件,里面的图片最终还是交给我们前面提到的file-loader来处理。

我们先按以下目录结构来创建项目

icoding
├─ node_modules
├─ package-lock.json
├─ package.json
├─ src
│  ├─ css
│  │  └─ basic.css
│  ├─ images
│  │  ├─ course.jpg
│  │  ├─ go.png
│  │  └─ logo.png
│  ├─ index.html
│  └─ js
│     └─ index.js
└─ webpack.config.js
  • index.html文件内容如下
<body>
  <img src="./images/logo.png" alt="" />
  <img src="./images/course.jpg" alt="" />
</body>
  • js/index.js文件内容如下
console.log("index.js");
  • package.json中部分信息如下
 "devDependencies": {
    "file-loader": "^6.2.0",
    "html-webpack-plugin": "^5.5.0",
    "html-withimg-loader": "^0.1.16",
    "url-loader": "^4.1.1",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1"
  }

# 3.1、html-withimg-loader 的使用

执行以下命令,安装html-withimg-loader对应的包

npm install html-withimg-loader --save-dev

然后在 webpack 的module.rules选中添加如下配置

module: {
  rules: [
    // ....
    {
      test: /\.(htm|html)$/i,
      loader: "html-withimg-loader",
    },
  ];
}

最后webpack.config.js文件内容如下:

const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  mode: "none",
  entry: "./src/js/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "./js/bundle.js",
  },
  module: {
    rules: [
      {
        // 根据正则表达式来匹配要处理的文件类型
        test: /\.(png|jpe?g|gif)$/i,
        use: {
          // 处理的loader名
          loader: "url-loader",
          // 参数设置
          options: {
            // 当图片的大小小于10kb时,会将其转成Base64编码格式的DataURL
            limit: 10240, // 10240=1024*10 相当于10k大小
            name: "[name].[ext]", // 图片的名字
            // 图片的输出目录,相对于output.path而言
            outputPath: "./images/",
            // 图片的预览地址,相对于被调用者与ouput.publicPath机制一样
            publicPath: "/dist/images/",
          },
        },
      },
      {
        test: /\.(htm|html)$/i,
        loader: "html-withimg-loader",
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
      chunks: "[name]",
    }),
  ],
};

然后,执行npx webpack打包,就可以看到 html 文件中的图片被成功处理了。但是发现处理后的地址不对,如下:

<img src={"default":"/dist/images/logo.png"} alt="" /> <img
src={"default":"/dist/images/course.jpg"} alt="" />

是因为file-loaderurl-loader在处理这个地址时,采用的是 ES6 的模块导入的,所以我们需要在他们的配置中,添加rules.use.options.esModule:false配置,具体如下:

rules:[{	// 根据正则表达式来匹配要处理的文件类型
    test: /\.(png|jpe?g|gif)$/i,
        use: {
            // 处理的loader名
            loader: 'url-loader',
                // 参数设置
                options: {
                    esModule:false,
                    // ......

                }
        }
}],

接下来,再执行npx webpack打包,打包后再浏览器端打开 html 文件,就可以正常看到图片被成功加载。

image-20230325192954652

# 4、Asset Modules 资源模块

TIP

Asset Modules 翻译成中文为 “资源模块” 的意思,他是 Webpack5 内置的模块类型,它允许 Webpack 处理资源文件(字体,图标等)而无需配置额外 loader。相当于 Webpack5 已经内置前面提到的file-loaderurl-loader对图片、字体等这一些型文件的处理能力。

我们只需要在 Webpack 的module.rules配置选项中配置相关的信息,就可以完成对图片,字体等这一类型文件的处理。

Asset Modules 是 Webpack 的未来,随着 Asset Modules 功能的不断完善,未来我们将不会再使用之前提到的file-loader、url-loader等对文件资源处理的 loader,而完全用 Asset Modules 来取代。但目前还不完善,但我们需要学习和了解它。

# 4.1 Asset Modules 配置

TIP

Asset Modules 的相关配置 查阅官方资料 (opens new window)

以下为 Asset Modules 在 Webpack 中的几个重要配置

rules: [
  {
    test: /\.(jpe?g|png|gif)$/i,
    type: "asset",
    generator: {
      filename: "images/[hash][ext]",
    },
    parser: {
      dataUrlCondition: {
        maxSize: 10 * 1024,
      },
    },
  },
];

以上配置说明:

  • test:需要被以下规则处理的符合条件的文件。上面/\.(jpe?g|png|gif)$/i表示匹配所有以jpg、jpeg、png、gif结尾的图片。
  • type: 采用那种方式来处理 test 中匹配到的资源模块,他的值有很多,这里主要讲解与图片相关的 3 种类型,详细查阅资料 (opens new window)
类型值 说明
asset/resource 这种类型用来替换之前file-loader的操作。它处理文件导入地址并将其替换成访问地址,同时把文件输出到相应位,
asset/inline 这种类型用来替换之前url-loader的操作。它处理文件导入地址并将其替换成 Base64 编码格式的 DataURL 地址。
asset 在单独导出文件和生成 DataURL 间自动选择。用来替换之前通过url-loader,并配置资源体积来实现的功能。
  • generator.filename 用来设置文件的名称。默认文件名格式是:[hash][ext][query]。也可以通过output.assetModuleFilename来设置,如:
output: {
  // ...
  assetModuleFilename: 'images/[hash][ext][query]'
 },

generator.filenameoutput.assetModuleFilename 功能相同,并且仅适用于 assetasset/resource 模块类型

  • parser.dataUrlCondition.maxSize 用来设置文件的大小,Webpack 会根据这个大小来决定打包的图片资源是以文件输出还是替换成 Base64 编码格式的 DataURL

接下来,我们通过案例来演示下

// 项目目录
icoding
├─ node_modules
├─ package-lock.json
├─ package.json
├─ src
│  ├─ css
│  │  └─ basic.css
│  ├─ images
│  │  ├─ course.jpg
│  │  ├─ go.png
│  │  └─ logo.png
│  ├─ index.html
│  └─ js
│     └─ index.js
└─ webpack.config.js
  • js/index.js文件内容
import "../css/basic.css";
console.log("index.js");
  • css/basic.css文件内容
/*basic.css 文件内容*/
.box1 {
  width: 100px;
  height: 100px;
  background: url("../images/course.jpg");
  background-size: contain;
}

.box2 {
  width: 100px;
  height: 100px;
  background: url("../images/logo.png");
  background-size: contain;
}
  • index.html 文件内容
<body>
  <div class="box1"></div>
  <div class="box2"></div>
</body>
  • images文件夹中的图片course.jpg20KB,logo.png8.7KB
  • 以下是本项目的package.json文件的部分内容
"devDependencies": {
    "css-loader": "^5.2.7",
    "html-webpack-plugin": "^5.5.0",
    "mini-css-extract-plugin": "^2.7.5",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1"
  }
  • 以下是webpack.config.js配置文件内容
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  mode: "none",
  entry: "./src/js/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "./js/bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
      {
        test: /\.(jpe?g|png|gif)$/i,
        type: "asset",
        generator: {
          filename: "images/[hash][ext]",
        },
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024, // 10KB  1KNB=1024B(字节)
          },
        },
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "./css/[name].[contenthash].css",
    }),
    new HtmlWebpackPlugin({
      template: "./src/index.html",
      chunks: "[name]",
    }),
  ],
};

执行npx webpack打包后,生成的dist目录结构如下:

 dist
  ├─ css
  │  └─ main.046c20ebf2a399661193.css
  ├─ images
  │  └─ 2715c2f1ee2c4063d887.jpg
  ├─ index.html
  └─ js
     └─ bundle.js

最后在浏览器中打开 index.html,展示效果如下

image-20230325181909805

注:

最终logo.png转成了 Base64 的 DataURL,course.jpg被复制到dist/images/目录下

# 五、CSS 样式兼容型处理

TIP

深入浅出 CSS 样式兼容型处理

# 1、PostCSS

TIP

PostCSS 是一个允许使用 JS 插件转换样式的工具。 这些插件可以检查(lint)你的 CSS,支持 CSS Variables 和 Mixins, 编译尚未被浏览器广泛支持的先进的 CSS 语法,内联图片,以及其它很多优秀的功能。

我们在书写 CSS 样式式,为了考虑不同浏览器的兼容问题,很多时候需要对 CSS 样式添加浏览器厂商私有前缀。而 postCSS 就是帮我们做这件事的。这样我们在书写 CSS 样式时,就不用担心没有添加浏览器厂商私有前缀而造成兼容问题。

PostCSS 中文文档 (opens new window)

比如:我们书写了下面这样一段 css 样式

.box {
  display: flex;
  width: 100px;
  height: 100px;
  background-color: red;
}

进过 postcss 处理后,就会自动帮我们加上浏览器的前缀,处理后 css 如下

.box {
  display: -webkit-box;
  display: -webkit-flex;
  display: -moz-box;
  display: -ms-flexbox;
  display: flex;
  width: 100px;
  height: 100px;
  background-color: red;
}

# 1.1、postcss-loader 的使用

TIP

要使用 PostCSS 这个工具来给样式添加不同浏览器的前缀,需要安装postcss-loaderpostcss两个包

执行以下命令,安装所需要的包

npm install --save-dev postcss-loader@7.1.0 postcss@8.4.21

安装后,我们修改下webpac.config.js文件,内容如下:

const path = require("path");
module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "./bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.(s[ac]ss|css)$/i,
        use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"],
      },
    ],
  },
};

注:

然后在当前根目录执行npx wepack执行打包,打包后发现生成的 CSS 样式中的并没有任何变化。

是因为我们还需要借助autoprefixer插件,告诉 postcss,我们需要把 CSS 处理成兼容哪些浏览器版本的 CSS。

# 1.2、安装 autoprefixer 插件

接下来,执行以下命令安装插件

npm i autoprefixer@10.4.14 --save-dev

安装成功后,在当前根目录下,新建 postcss 的配置文件postcss.config.js,添加以下配置

const autoprefixer = require("autoprefixer");

module.exports = {
  plugins: [
    autoprefixer({
      browsers: ["cover 99.5%"],
    }),
  ],
};

或把autoprefixer插件中的参数去掉,直接在package.json中来配置浏览器信息,添加"browserslist"字段,具体如下:

{
  "devDependencies": {},
  "browserslist": ["cover 99.5%"]
}

注:

还有其它方式,关于浏览器相关配置,可查阅文档 (opens new window)

最后在当前工作目录执行npx webpack打包,打包后我们在浏览器中打开 index.html 页面

具体信息看以下截图

image-20230325025100158

# 六、处理 JS 中的兼容问题

TIP

前面我们学习过 Babel,利用 Babel 将 ES6 的代码转找成 ES5 或 ES3 的代码,但是我们是将所有 ES6 的语法部分全部转换成了 ES5 或 ES3,并没有指定按目标环境来转换。比如:只考虑 firefox 版本>50 的浏览器中缺失的那部分 ES6 语法才转换等。

同时,我们之前利用 Babel 只能转换 ES6 的语法,对于缺失的 API,我们只能通过引入 polyfill 来实现,并不能实现按指定目标环境来引入缺失的 API。

以上这两个核心问题,就是接下来我们要重点解决的。

我们先回顾前面讲过的内容,比如:

  • Babel 的配置文件
  • Babel 的预设包
  • Babel 官方的 polyfill
  • 利用 webpack 与 bable-loader 来实现 Babel 转码

然后,我们再来学习如何利用 Babel 实现按需转码(即:按指定的目标环境来转码)

# 1、Babel 的配置文件

TIP

前面我们已经了解过 Babel,并且也使用过 Babel 的配置文件,这里我们对于配 Babel 的配置文件做相关补充。

详细资料参考官方:https://babeljs.io/docs/configurationc (opens new window)

Babel 的配置文件支持很多种格式,比如babelr.json、babel.config.json、babel.config.js、.babelrc.js。当然我们也可以在package.json来配置 Babel。

接下来我们挑几个我们常用的说下

以下所有配置文件中的"presets"中用来配置 Babel 预设,"plugins"中用来配置插件

# 1.1 、babel.config.json 文件

在当前项目的根目录下创建babel.config.json文件,其配置格式

{
  "presets": [...],
  "plugins": [...]
}

# 1.2、babel.config.js 文件

在当前项目的根目录下创建babel.config.js文件,其配置格式

module.exports={
    presets: [...],
    plugins: [...]
}

# 1.3、package.json 文件

package.json文件中配置格式如下

{
  "name": "my-package",
  "version": "1.0.0",
  "babel": {
    "presets": [ ... ],
    "plugins": [ ... ],
  }
}

# 2、Babel 中的预设

TIP

在前面我提到过 Babel 中预设,我们说预设就是一组 Babel 插件的集合,我们想把 ES6 的语法转换成 ES5 或 ES3 需要用到对应的插件来帮我们转,而这些相关的插件非常多,所以我们就把他们统一到一个包中,这个包就是@babel/preset-env预设。

@babel/preset-env 预设的相关配置文档 (opens new window)

所以我们需要将 ES6 的语法转换成 ES5 或 ES3 语法,只需要在 Babel 的配置文件中配置这个预设就可以了。以下是babel.config.js配置文件内容:

module.exports = {
  presets: ["@babel/preset-env"],
};

如果在package.json中配置如下:

"babel": {
    "presets": [ "@babel/preset-env" ]
  }

注:

但是我们说,@babel/preset-env预设只能帮我转换 JS 中的语法,但对于 API 本身是没有办法转的,所以我们需要借助 polyfill 来帮我们实现。

# 3、Babel 官方的 polyfill

TIP

在 Bable7.4.0 以前,Babel 官方提供的polyfill包名为@babel/polyfill,但现在官方已经把这个包给弃用了。@babel/polyfill包本身是regenerator-runtimecore-js这两个包组成的。

因为 core-js 这个包的版本已经更新到 3.x.x.x 版本,而@babel/polyfill里的 core-js 已经锁死为 2.x.x.x 版本的。

所以官方推荐我们在开发中,独立安装core-jsregenerator-runtime包,并在你的 JS 中独立加载他们。

  • core-js为核心包,几乎所有 API 接口的实现都在这个包里。
  • regenerator-runtime是对编译/转译 async 函数的运行时支持(它可能还有其他用途,但这是主要用途)。

官方文档参考:https://babeljs.io/docs/babel-polyfill (opens new window)

执行以下命令安装 polyfill 对应的包

npm i core-js@3.24.1  regenerator-runtime@0.13.11

安装好后,我们只需要通过import语句在需要的 JS 文件中,通过以下句语来引用这两个包就好

import "core-js/stable";
import "regenerator-runtime/runtime";

# 4、webpack 与 babel-loader 实现转码

TIP

接下来,我们结合 Webpack 在项目利用 Babel 来 JS 文件进行转码后再打包。

项目结构如下:

icoding
├─ node_modules
├─ package-lock.json
├─ package.json
├─ src
│  ├─ index.html
│  └─ index.js
└─ webpack.config.js

index.js文件内容如下:

import "core-js/stable";
import "regenerator-runtime/runtime";

const obj = Object.assign({ a: 1 }, { b: 4 });
console.log(obj);

index.html文件内容如下:

<body>
  <script src="../dist/bundle.js"></script>
</body>

webpack.config.js文件配置内容如下:

const path = require("path");
module.exports = {
  mode: "none",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          /* 预设可以直接设置在这里,也可以在`package.json`或bable的配置文件中
                options:{
                    "presets": [
                        "@babel/preset-env"
                    ]
                }
                */
        },
      },
    ],
  },
};

babel.config.js文件内容如下:

module.exports={
    presets: ["@babel/preset-env"]
}

// 我们也可以把`bable`的配置设置在`package.json`中,如下
 "babel": {
    "presets": [
      "@babel/preset-env"
    ]
  }

package.json文件核心内容如下

"devDependencies": {
    "@babel/core": "^7.21.3",
    "@babel/preset-env": "^7.20.2",
    "babel-loader": "^9.1.2",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1"
  },
  "dependencies": {
    "core-js": "^3.24.1",
    "regenerator-runtime": "^0.13.11"
  },

接下来在当前工作目录执行npx webpack完成打包工作,然后在firefox 32版本的浏览器中来浏览,发现控制台正确打印出以下结果:

Object { a: 1, b: 4 }

Object.assign()方法,在大于>=34 版本的 firefox 中才被支持

# 5、实现 Babel 按需转码

TIP

当我们在 Babel 中使用了@babel/preset-env预设时,我们可以为这个预设配置相关的参数,通过这些参数我们可以设置只针对目标环境不支持的语法进行语法转换。

同时也可以与 polyfill 结合,只针对目标环境不支持的特性API进行部分引用,这样我们就不用把完整的 polyfill 全部引入到最终的文件。

以下是@babel/preset-env预设的部分重要参数,也是我们着重要撑握的参数。

参数 说明
targets 这个参数用来设置 Babel 转码的目标环境,也就是指定我们转码后的代码需要兼容那些版本的浏览器。他与前面讲到的browserslist功能一样,设置也是一样。
useBuiltIns 这个参数的作用主要是根据targets中的设置(目标环境),找出需要的 polyfill 进行部分引入。
corejs 指定 Babel 转码时使用的 core-js 的版本,默认不设置为 2.x.x.x 版本。不过建议使用 3.x.x 版本。 这个值只在 useBuiltIns 的值是entryusage时才生效。
注意:如下不指定,默认为 2.x.x,则对应的 polyfill 为@babel/polyfill。如果为 3.x.x 版本,则需要手动安装@core-js@3.x.x的包。

参数设置

@babel/preset-env预设需要设置参数时,预设就不能以字符串形式直接放在数组中,而是应该再包裹一层数数组,数组的第一项是该预设的名称字符串,数组的第二项是一个对象,该预设的参数就是添加在这个对象中。具体参考以下格式

// babel.config.js
module.exports = {
  presets: [
    ["@babel/preset-env"],
    {
      // 预设参数设置在这里
    },
  ],
};

# 5.1、targets 参数

TIP

这个参数用来设置 Babel 转码的目标环境,也就是指定我们转码后的代码需要兼容那些版本的浏览器。他与前面讲到的browserslist功能一样,设置也是一样。

也就是说,我们可以通过@babel/preset-env预设的targets参数来指定 Babel 转码的目标环境,也可以在package.json中通过browserslist字段来设置。

如果我们在@babel/preset-env预设中设置了targets参数,那 Babel 转码时就会以这里指定的为主。如果这里没有指定,就会使用package.jsonbrowserslist中的配置。如果package.json中也没有配置,那默认就会将所有 ES6 语法转换成 ES5 语法。

@babel/preset-env预设的 targets 参数,只能实现语法按指定目标环境来转码,并不能实现 API 转换。

targets 参数的值可是字符串,数组、对象都可以。

targets 参数设置参考文档:https://babeljs.io/docs/options#targets (opens new window)

关于浏览器兼容性列表查询:https://github.com/browserslist/browserslist (opens new window)

代码演示

我们以上一个项目为基础,修改index.js文件内容如下

// async 函数 在firefox 第52版本才开始支持
async function fn() {
  console.log(1);
}
fn();

以下为当前项目的babel.config.js的配置文件内容

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        // 只考虑firefox版本大于53的浏览器
        targets: "Firefox > 53",
      },
    ],
  ],
};

注:

接下来在当前目录下执行npx webpack打包,打包后,在firefox32版的浏览器中打开index.html页面,发现控制台抛出错误。因为当前浏览器并不支持async函数,而我们打包时指定转换的Firefox>53版本,也就是不考虑 53 及以下版本的兼容型的处理。

如果我们把配置改为targets:"Firefox > 30",然后再次打包,打包后刷新浏览器,在控制台成功输出 1

当然,我们为了考虑和 CSS 指定相同的目标环境,我们一般会不会在这里设置 targets 属性,而是在package.json中,通过属性来指定。

# 5.2、useBuiltIns 参数

TIP

这个参数的作用主要是根据上面提到的targets参数中的设置或package.json中的browserslist设置,找出需要的 polyfill 进行部分引入。

他有三个值,3 个值及具体作用如下表

属性值 说明
false 表示引入全部的 polyfill,即(core-js 和 regenerator-runtime)包中的所有代码
entry 会根据targets配置的目标环境 或browserslist中的配置,找出缺失的 API,然后从 polyfill 中只引入缺失部分的 API 代码。
usage 他和 entry 一样,会根据目标环境引入缺失的 API,还会考虑使用的 ES6 特性 API 在目标环境中缺失问题。如果项目中使用的某些 API 在目标环境中缺失,他也会把这部分 API 给引入进来

注意事项

useBuiltIns 参数虽然是作为@babel/preset-env预设的参数,但是他主要是解决 JS 中 API 的兼容问题。

所以我们要让配置生效,一定要下载@bable/polyfill包或手动下载core-js@3.x.xregenerator-runtime/runtime包,并且还需要在对应的 JS 中通过 import 来引用他们。

代码演示

接下来我们在之前的项目基础上,修改index.js文件内容如下

import "core-js/stable";
import "regenerator-runtime/runtime";

// 只在firefox>=92的浏览器中生效
const example = {};
// 对象自身是否有指定的属性
const bool = Object.hasOwn(example, "prop");
console.log("值为", bool);

// 只在firefox>=34的浏览器中生效
const obj = Object.assign({ a: 1 }, { b: 4 });
console.log(obj);

修改babel.config.js配置文件内容如下

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        // 只考虑firefox版本大于35的浏览器
        targets: "Firefox > 35",
        useBuiltIns: "entry",
        // 这里不指定corejs的版本,默认采用core-js@2.x.x版本,而本案例中安装的是3版,所以要指定,否则打包后会与预期的效果不一样。
        corejs: "3.24.1",
      },
    ],
  ],
};

注:

接下来,我们在当前目录下执行npx webpck打包,打包后在浏览器中访问index.html页面,在控制台 输出结果如下截图。同时我们在控制台执行[1,2,[3,[4],5]].flat(3)代码,最终被成功执行。

image-20230326232037939

数组的 flat 方法在firefox>=63版本的浏览器中,才原生支持,所以也被成功处理了兼容型。

接下来,我们把useBuiltIns的值改为"usage",再次打包,然后刷新浏览器,在控制台输出的结果如下图。同时我们在控制台执行[1,2,[3,[4],5]].flat(3)代码,最终被成功执行。

image-20230326232310256

通过控制台输出的结果,我相信你已经能够区分entryusage的区别了。所以简单一点,entry 会根据给定的目标环境来引入缺失的 API,而usage除了会考虑目标环境中缺失的 API,还会考虑项目中实际使用的 API 是否在目标环境中缺,如果缺失,也会引入这部分 API。

# 5.3、corejs 参数

TIP

通过上面的学习,我们应该知道 corejs 参数的作用了。他主要用来指定 Babel 转码时使用的 core-js 的版本。默认不设置时,值为 2.x.x 版本,这时候我们需要安装的 polyfill 为@babel/polyfill

如果设置值为 3.x.x 版本,则需要手动安装core-js@3.x.xregenerator-runtime两个包。

这个值只在 useBuiltIns 的值是entryusage时才生效。

# 七、总结

TIP

本章重点掌握 loader 与 plugins 是如何配置的。在 Web 前端领域里,我们只需要掌握以下几个方向的 loader 和插件

处理 JS 需要用到的 loader 与插件

  • babel-loader

处理 CSS 需要用到的 loader 与插件

  • css-loader 与 style-loader
  • sass-loader
  • min-css-extract-plugin 插件

处理图片需要用到的 loader

  • file-loader 与 url-loader
  • html-withimg-loader
  • Asset Modules 资源模块

处理 html 文件需要用到的插件

  • html-webpack-plugin

webpack 开发环境下用到的工具插件

  • clean-webpack-plugin

Web 前端 CSS 与 JS 兼容处理

  • 掌握通过使用postcss-loader预处理器与autoprefixer插件来解决 CSS 兼容问题
  • 掌握如何利用 Babel 来处理 JS 中的兼容问题(其中包括语法和 API 的兼容)
上次更新时间: 6/8/2023, 9:23:17 PM

大厂最新技术学习分享群

大厂最新技术学习分享群

微信扫一扫进群,获取资料

X