这边文章主要想记录一下 在一个项目中关于babel 的基本配置 包括 preset-env 配置 useBuiltIns 的用法 以及 @babel/plugin-transform-runtime 插件的使用 弄明白 为什么要配置这些?弄明白每一个配置的含义
1 准备工作
首先 创建项目 -> npm 初始化 -> 安装依赖
mkdir babel-practice
cd babel-practice
npm init -y
npm install @babel/core @babel/cli @babel/preset-env -D
2 创建目录src 以及 package.json scripts 配置
"scripts": {
"start": "babel src --out-dir lib"
}
3 尝试转译 一个文件
// src/index.js
const arr = [1,2,3,4,5]
const arr1 = arr.filter((item)=>{
return item > 2
})
arr1.includes(4)
//转译结果
// lib/index.js
const arr = [1, 2, 3, 4, 5];
const arr1 = arr.filter(item => {
return item > 2;
});
arr1.includes(4);
如果没有配置preset-env 则会原封不动的输出 所以需要配置 preset
4 preset-env 配置
创建 babel.config.js
module.exports = {
presets:[
"@babel/preset-env"
]
}
再次转译
// src/index.js
const arr = [1,2,3,4,5]
const arr1 = arr.filter((item)=>{
return item > 2
})
arr1.includes(4)
//转译结果
// lib/index.js
var arr = [1, 2, 3, 4, 5];
var arr1 = arr.filter(function (item) {
return item > 2;
});
arr1.includes(4);
可以看到 const 以及 箭头函数 被转译了 而 arr.includes 没有被转译
原因:
babel 在转译的时候,会将源代码分成 syntax 和 api 两部分来处理
Babel 默认只转换新的 JavaScript语法,比如const let 箭头函数,而不转换新的API。例如,Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法 比如:includes Array.from 等都不会转译。 如果想使用这些新的对象和方法,必须使用 polyfill ( 或者 @babel/runtime ) 来处理
5 配置 useBuiltIns
我们先使用 'entry' + import "@babel/polyfill" 方式:
// babel.config.js
module.exports = {
presets:[
[
"@babel/preset-env",
{
"useBuiltIns": "entry"
}
]
]
}
// index.js
import "@babel/polyfill"
const arr = [1,2,3,4,5]
const arr1 = arr.filter((item)=>{
return item > 2
})
arr1.includes(4)
转译结果:
如图所示:
这种方式引入了全量的 polyfill(包括 core-js/modules/xxxx 和 regenerator-runtime/runtime.js )
但是我们在这里只需要引入支持includes 和 filter 的polyfill 即可
所以采用 按需引入的方式 "useBuiltIns": "usage"
// babel.config.js
module.exports = {
presets:[
[
"@babel/preset-env",
{
"useBuiltIns": "usage"
}
]
]
}
结果:
貌似比较好的解决了includes的转译问题了 但是 这里有个问题
arr.includes 本身并没有发生变化 通过 require polyfill 的方式只是在Array.prototype上添加了 includes 方法 (Array.prototype.includes )
上面这种方式污染了 Array 以及 Array.prototype 这种污染全局属性的方式一般在编程理念中是不推荐的
再有像 class 以及 Async 这些特殊语法在被转译时 babel会在当前文件中定义一些辅助方法来实现转译 这些方法被称为 helpers 拿 class 尝试一下
// src/class.js
class Abc{
constructor(name) {
this.name = name
}
}
const abc = new Abc('abc')
转译结果:
图中可以看到_createClass _classCallCheck方法在当前文件被定义 这里也有个问题
如果我在多个文件中都定义了 class xxx (这是很常见的) 那岂不是所有的文件中 都有一份这样的定义 这样就太糟糕了
该如何解决?
6 @babel/plugin-transform-runtime 插件
@babel/plugin-transform-runtime这个插件的作用就是解决上面提到的两个问题
它的作用是 通过模块化的方式 从runtime中引入对应的转译方法 在不污染全局作用域的同时也不会在每个文件中重复定义
如果使用了 @babel/plugin-transform-runtime 则不再需要配置 "useBuiltIns": "usage"
先安装插件:
npm install @babel/plugin-transform-runtime -D
在 babel.config.js 中修改配置
```js
module.exports = {
presets:[
[
"@babel/preset-env"
]
],
plugins:[
"@babel/plugin-transform-runtime"
]
}
src/class.js 中 转译的结果如下:
但是 src/index.js 的转译结果貌似有些异常 filter 和 includes 未被转译
导致这个问题的原因是因为我们再使用 @babel/plugin-transform-runtime 时没有配置 corejs corejs 配置可选项 为 false / 2 / 3 分别对应的不同的运行时插件来处理
corejs的默认配置是false
在这里有必要先了解一下这几个包的关系
- @babel/polyfill
- @babel/runtime
- @babel/runtime-corejs(2|3)
- @babel/plugin-transform-runtime
- @babel/polyfill仅仅是引用core-js、regenerator-runtime这两个npm包。
- @babel/runtime包含两个文件夹:helpers(定义了一些处理新的语法关键字的帮助函数)、regenerator(引用regenerator-runtime)
- @babel/runtime-corejs2 包含三个文件夹:core-js、helpers、regenerator @babel/runtime-corejs2 ≈ @babel/runtime + babel-polyfill:
- 如果没有 @babel/plugin-transform-runtime 则 @babel/runtime相当于是手动挡 需要手动引入 对应的 helpers @babel/plugin-transform-runtime 能帮助你自动引入
@babel/runtime / @babel/runtime-corejs(2|3) 对比
@babel/runtime只能处理语法关键字
@babel/runtime-corejs2 支持全局变量(例如 Promise)和静态属性(例如 Array.from)但不支持 实例属性
@babel/runtime-corejs3 支持实例属性(例如 [].includes)。
了解完这些后继续回到上面的问题 由于@babel/plugin-transform-runtime 的 corejs 没有配置 默认为false 对应的runtime为@babel/runtime 他只能处理语法关键字 没法对 includes 等实例属性转译 因此此处需要配置 corejs: 3
module.exports = {
presets:[
[
"@babel/preset-env"
]
],
plugins:[
[
"@babel/plugin-transform-runtime",
{
corejs: 3
}
]
]
}
配置corejs:3 后的转译结果为:
这一步终于完成了最基本的配置了
后续有时间再继续探讨