「这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战」
1. Webpack 中使用 Babel
前面我们讲了 Babel 在命令行中的使用以及 Babel 的插件和预设的使用,这也是单独使用 Babel 的方式。下面,我们来讲下如何在 Webpack 中使用 Babel。
首先,有个问题,Webpack 中为什么要用 Babel 呢?我们可以先来做一个测试,我们把之前在项目中添加的 demo.js 文件中的代码拷贝一份,放到 ./src/main.js 文件的最后:
...
const message = "Hello World";
const names = ['王毅', '朱广权', 'zhj'];
names.forEach(item => console.log(item));
然后,我们执行 npm run build 命令对项目中的文件进行打包,打包后我们打开输出的 ./build/js/bundle.js 文件,在文件中搜索 message:
你会发现,Webpack 虽然对我们编写的 js 代码打包了,但并没有将 ES6 语法的代码转换成 ES5 语法的代码。那就意味着,假如我们现在部署这个 ./build 文件夹,如果用户的浏览器不支持 ES6 的代码,到时候这个代码就会无法正常运行。所以我们希望 Webpack 在打包时帮助我们将 ES6 的代码转成 ES5 的代码。当然,你可能会问,Webpack 打包时为什么没有把 ES6 转换成 ES5 呢?原因非常简单,因为这并不是 Webpack 本身的工作,我们知道,Webpack 是一个模块化的打包工具,它只负责把资源当成模块(默认支持 ESM、CommonJS 等模块类型)来帮助我们对这些模块全部进行打包,而不负责在打包过程中对代码做一些转化(比如把 ES6+ 代码转成 ES5 代码,把 TypeScript 代码转成 JavaScript 代码,对 React 代码进行转化等等)。
如果我们想要 Webpack 在打包时将 ES6 的代码转成 ES5 的代码,就需要在 Webpack 中去使用 Babel 了,让 Babel 去做代码转换的工作。那在什么时候使用 Babel 呢?显然,是在加载 .js 文件(或 .mjs 文件即 ES Module 文件)时(如果 js 代码中有 ES6 的代码,将其转成对应的 ES5 的代码),我们要使用一个 loader:babel-loader,这个 loader 可以将 Babel 和 Webpack 结合起来,它内部本质上用的就是 Babel,只不过是以代码的方式做了一个引用(转化)去使用 Babel 的。那么我们先来安装它:
npm install babel-loader -D
注意:事实上,
@babel/core也需要安装,只不过我们前面已经安装过了,所以这里没有再安装。
安装完成后,我们来配置 webpack.config.js 文件:
...
module.exports = {
...
module: {
rules: [
...
{
test: /\.m?js$/,
loader: 'babel-loader'
}
]
},
...
}
我们添加了一条加载 js 文件时使用 babel-loader 进行处理的规则,再来打包,bundle.js 文件中的效果如下:
你会发现,ES6 的代码依然没有转换为 ES5 的代码,那为什么这次依然没有帮助我们进行转化呢?其实原因很简单,我们这次在使用 babel-loader 时没有使用任何插件,而只有使用了对应的插件才能让 Babel 帮助我们完成对应的事情,如果没有使用任何插件,则意味着在 babel 的整个编译流程走完之后其实是没有对代码做任何的转化的。因此,我们还需要指定要使用的 babel 插件:
...
module.exports = {
...
module: {
rules: [
...
{
test: /\.m?js$/,
use: {
loader: 'babel-loader',
options: {
plugins: [
'@babel/plugin-transform-block-scoping',
'@babel/plugin-transform-arrow-functions'
]
}
}
}
]
},
...
}
注意:这些
babel插件也需要安装,只不过我们前面安装过了,所以这里没有再做安装。
修改完配置后,我们再来打包,效果如下:
可以看到,这次使用了 babel 对应的插件之后,相应的 ES6 语法代码就被成功转换成了 ES5 语法的代码。
至此,我们就知道了 babel-loader 的强大是因为其依赖于 babel,而 babel 又是在内部通过其各种插件帮助我们转化代码。
上面我们配置时是一个个地使用插件,而我们前面讲过,如果每次都这样一个个地安装、使用插件,那么需要手动管理大量的 babel 插件,这太麻烦了,所以我们还可以直接使用预设(Presets),我们可以直接给 webpack 提供一个 preset,webpack 会自动根据我们的预设来加载对应的插件列表,并且将其传递给 babel,比如我们这里需要使用能够编译 ES6+ 语法代码的预设:
...
module.exports = {
...
module: {
rules: [
...
{
test: /\.m?js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
]
}
}
}
]
},
...
}
注意:
babel预设也需要安装哦,只不过我们前面安装过了,所以这里没有再做安装。
修改完配置后,我们再来打包,效果如下:
可以看到,也是可以成功转化的。
补充:
有时候,你可能会看到
babel-loader的options.presets选项对应的数组中放的又是数组,像这样:presets: [ ['@babel/preset-env', { targets: "defaults" }] ]这种写法中的
{ targets: "defaults" }对象表示对@babel/preset-env预设进行的一些额外配置。也就是说,如果我们想给某个
preset传递一些额外的参数时,就需要写成一个数组,在数组的第二项(对象)中进行传参。
1.1 Babel 的配置文件
之前我们讲 PostCSS 时,讲到过可以将 PostCSS 的配置从 Webpack 的配置文件中抽取到独立的文件中。其实 Babel 的配置信息也可以抽取到独立的文件中。
babel 给我们提供了两种配置文件格式:
- 项目范围的配置(
Project-wide configuration):babel.config.json(或者扩展名为.js、.cjs、.mjs)文件;
- 文件相关的配置(
File-relative configuration):.babelrc.json(或者扩展名为.babelrc、.js、.cjs、.mjs)文件(这里rc表示的是runtime compiler);- 有一个键为
babel的package.json文件;
这两种配置文件格式有什么区别呢?目前很多项目(babel 本身、element-plus、umi 等)都采用了多包管理的方式;
.babelrc.json:早期使用较多的配置方式,但是对于配置Monorepos项目时比较麻烦的;babel.config.json(Babel 7新增的):可以直接作用于Monorepos项目的子包,更加推荐使用;
那下面我们就来编写 Babel 的配置文件吧,我们在项目目录(webpack_babel)下新建 babel.config.js 文件,文件内容如下:
module.exports = {
presets: [
'@babel/preset-env'
]
}
然后,之前 webpack.config.js 文件中对于使用 babel-loader 时的相关配置就可以去掉了,直接使用 babel-loader 即可:
...
module.exports = {
...
module: {
rules: [
...
{
test: /\.m?js$/,
loader: 'babel-loader'
}
]
},
...
}
再来重新执行 npm run build 命令编译打包代码,./build/js/bundle.js 文件中的效果如下:
可见,依然能将 ES6 语法的代码成功转换为 ES5 语法的代码。
以上,就是关于 webpack 中 babel-loader 以及 babel 单独的配置文件的使用过程。