Babel到底是什么呢?Babel是一个工具链,主要用于旧浏览器或者缓解中将ECMAScript 2015+代码转换为向后兼容版本的JavaScript;包括:语法转换、源代码转换、Polyfill实现目标缓解缺少的功能等;
- babel本身可以作为一个独立的工具(和postcss一样),不和webpack等构建工具配置来单独使用。如果我们希望在命令行尝试使用babel,需要安装如下库:
- @babel/core:babel的核心代码,必须安装;
- @babel/cli:可以让我们在命令行使用babel;
npm install @babel/cli @babel/core
- 使用babel来处理我们的源代码:
- src:是源文件的目录;
- --out-dir:指定要输出的文件夹dist;
npx babel src --out-dir dist
- 比如我们需要转换箭头函数,那么我们就可以使用箭头函数转换相关的插件:
npm install @babel/plugin-transform-arrow-functions -D
npx babel src --out-dir dist --plugins=@babel/plugin-transform-arrow-functions
- 查看转换后的结果:我们会发现const 并没有转成var
- 这是因为plugin-transform-arrow-functions,并没有提供这样的功能;
- 我们需要使用plugin-transform-block-scoping 来完成这样的功能;
npm install @babel/plugin-transform-block-scoping -D
npx babel src --out-dir dist --plugins=@babel/plugin-transform-block-scoping,@babel/plugin-transform-arrow-functions
- 但是如果要转换的内容过多,一个个设置是比较麻烦的,我们可以使用预设(preset):后面我们再具体来讲预设代表的含义;
- 安装@babel/preset-env预设:
npm install @babel/preset-env -D
- 执行如下命令:
npx babel src --out-dir dist --presets=@babel/preset-env
-
babel是如何做到将我们的一段代码(ES6、TypeScript、React)转成另外一段代码(ES5)的呢?从一种源代码(原生语言)转换成另一种源代码(目标语言),这是什么的工作呢?就是编译器,事实上我们可以将babel看成就是一个编译器。Babel编译器的作用就是将我们的源代码,转换成浏览器可以直接识别的另外一段源代码;
-
Babel也拥有编译器的工作流程:
- 解析阶段(Parsing)
- 转换阶段(Transformation)
- 生成阶段(Code Generation)
我们可以设置一个规则,在加载js文件时,使用我们的babel:
{
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
// options: {
// 预设
// presets: [
// ["@babel/preset-env", {
// 通过传递给预设的参数 targets 来设置目标浏览器,targets 的优先级比 .browserslistrc 文件高
// // targets: ["chrome 88"]
// // enmodules: true
// }]
// ]
// 使用指定的插件
// // plugins: [
// // "@babel/plugin-transform-arrow-functions",
// // "@babel/plugin-transform-block-scoping"
// // ]
// }
}
}
]
}
在babel7之前(比如babel6中),我们会经常看到这种设置方式:
- 它表达的含义是使用对应的babel-preset-stage-x 预设;
- 但是从babel7开始,已经不建议使用了,建议使用preset-env来设置;
{
presets: ['stage-3']
}
像之前一样,我们可以将babel的配置信息放到一个独立的文件中,babel给我们提供了两种配置文件的编写:
- babel.config.json(或者.js,.cjs,.mjs)文件;
- .babelrc.json(或者.babelrc,.js,.cjs,.mjs)文件;
它们两个有什么区别呢?目前很多的项目都采用了多包管理的方式(babel本身、element-plus、umi等);
- .babelrc.json:早期使用较多的配置方式,但是对于配置Monorepos项目是比较麻烦的;
- babel.config.json(babel7):可以直接作用于Monorepos项目的子包,更加推荐;
为什么时候会用到polyfill呢?
-
比如我们使用了一些语法特性(例如:Promise, Generator, Symbol等以及实例方法例如Array.prototype.includes等)但是某些浏览器压根不认识这些特性,必然会报错;我们可以使用polyfill来填充或者说打一个补丁,那么就会包含该特性了;
-
babel7.4.0之前,可以使用@babel/polyfill的包,但是该包现在已经不推荐使用了:babel7.4.0之后,可以通过单独引入core-js和regenerator-runtime来完成polyfill的使用:
npm install core-js regenerator-runtime --save
我们需要在babel.config.js文件中进行配置,给preset-env配置一些属性:
-
useBuiltIns:设置以什么样的方式来使用polyfill;
-
corejs:设置corejs的版本,目前使用较多的是3.x的版本,比如我使用的是3.8.x的版本;
- 另外corejs可以设置是否对提议阶段的特性进行支持;
- 设置proposals属性为true即可;
-
useBuiltIns属性有三个常见的值
- 第一个值:false 打包后的文件不使用polyfill来进行适配;并且这个时候是不需要设置corejs属性的;
- 第二个值:usage 会根据源代码中出现的语言特性,自动检测所需要的polyfill;这样可以确保最终包里的polyfill数量的最小化,打包的包相对会小一些;可以设置corejs属性来确定使用的corejs的版本;
- 第三个值:entry 如果我们依赖的某一个库本身使用了某些polyfill的特性,但是因为我们使用的是usage,所以之后用户浏览器可能会报错;所以,如果你担心出现这种情况,可以使用entry;并且需要在入口文件中添加`import 'core-js/stable'; import 'regenerator-runtime/runtime';这样做会根据browserslist 目标导入所有的polyfill,但是对应的包也会变大;
// babel.config.js
module.exports = {
presets: [
["@babel/preset-env", {
// false: 不用任何的polyfill相关的代码
// usage: 代码中需要哪些polyfill, 就引用相关的api
// entry: 手动在入口文件中导入 core-js/regenerator-runtime, 根据目标浏览器引入所有对应的polyfill
useBuiltIns: "entry",
corejs: 3
}],
["@babel/preset-react"]
]
}
上面使用 polyfill 时使用的属性 useBuiltIns 是传递给 preset 的参数,它会使得 polyfill 是全局影响的。链接 如果不想 polyfill 被添加到全局,使用 plugin-transform-runtime 插件。在前面我们使用的polyfill,默认情况是添加的所有特性都是全局的。如果我们正在编写一个工具库,这个工具库需要使用polyfill;别人在使用我们工具时,工具库通过polyfill添加的特性,可能会污染它们的代码;所以,当编写工具时,babel更推荐我们使用一个插件: @babel/plugin-transform-runtime来完成polyfill的功能;
- 安装@babel/plugin-transform-runtime:
npm install @babel/plugin-transform-runtime -D
- 使用plugins来配置babel.config.js:
module.exports = {
plugins: [
["@babel/plugin-transform-runtime", {
corejs: 3
}]
]
}
而且要依据安装的 corejs 库版本安装对应的另外一个包:
corejs 选项 | 安装对应的包 |
---|---|
false | npm install --save @babel/runtime |
2 | npm install --save @babel/runtime-corejs2 |
3 | npm install --save @babel/runtime-corejs3 |
- 我们编写react代码时,react使用的语法是jsx,jsx是可以直接使用babel来转换的。对react jsx代码进行处理需要如下的插件:
- @babel/plugin-syntax-jsx
- @babel/plugin-transform-react-jsx
- @babel/plugin-transform-react-display-name
但是开发中,我们并不需要一个个去安装这些插件,我们依然可以使用preset来配置:
npm install @babel/preset-react -D
在 babel.config.js 中这样设置:
module.exports = {
presets: [
["@babel/preset-react"]
]
}
- 在项目开发中,我们会使用TypeScript来开发,那么TypeScript代码是需要转换成JavaScript代码。 可以通过TypeScript的compiler来转换成JavaScript:
npm install typescript -D
另外TypeScript的编译配置信息我们通常会编写一个tsconfig.json文件:
tsc --init
之后我们可以运行npx tsc来编译自己的ts代码:
npx tsc
- 如果我们希望在webpack中使用TypeScript,那么我们可以使用ts-loader来处理ts文件:配置ts-loader:
{
test: /\.ts$/,
exclude: /node_modules/,
// 本质上是依赖于typescript(typescript compiler)
use: "ts-loader"
}