由于 JavaScript 在不断地发展,各种新标准和提案也就层出不穷,用户使用的浏览器也不同(尤其是旧版的 IE),这就导致了有些用户无法使用 JavaScript 的一些新特性,如果开发人员想使用新的语法(如 class A {}),在旧浏览器上就会因为这个问题出现 SyntaxError。
由于它能够转换 JavaScript 代码,还可用于实现新功能,因此它已成为 TC39 获取社区对 JavaScript 反馈的重要桥梁。
如今,Babel 是 JavaScript 开发的基础。它在 npm 上,每月有 1700 万次的下载量,用户包括主流框架(React,Vue,Ember,Polymer)的开发者和企业(Facebook,Netflix,Airbnb)。即使你自己没有使用它,你的依赖项也很可能正在使用 Babel。
虽然 Babel 对 JavaScript 语言的未来,甚至对社区和 js 生态都产生了巨大的影响,但你肯定想不到,他们的维护者只是由社区的几个志愿者组成。
babel-upgrade 是一个用于进行自动升级的新工具:目前在 package.json 和.babelrc 配置文件中配置了依赖关系。
建议直接在 git 仓库上运行 npx babel-upgrade,或者使用 npm i babel-upgrade -g 进行全局安装。
如果你想修改文件,可以使用参数 --write 和 --install。
npx babel-upgrade --write --install
Babel 7.0 引入了 babel.config.js。
*.js 配置文件在 JavaScript 生态系统中相当常见。ESLint 和 Webpack 分别使用了.eslintrc.js 和 webpack.config.js 配置文件。但这并不意味着它们就取代了.babelrc 或 package.json,这不是必要条件,但在某些情况下这可能很有用。以下是仅在“生产”环境中使用插件进行编译的配置。
babel.config.js 的配置解析方式与.babelrc 不同。它始终会解析该文件中的配置,而不会从每个文件向上查找,直到找到配置为止。这样就可以利用 overrides 特性。
var env = process.env.NODE_ENV;
module.exports = {
plugins: [
env === "production" && "babel-plugin-that-is-cool"
].filter(Boolean)
};
module.exports = {
presets: [
// defeault config...
],
overrides: [{
test: ["./node_modules"],
presets: [
// config for node_modules
],
}, {
test: ["./tests"],
presets: [
// config for tests
],
}]
};
有些应用程序需要针对测试、客户端代码和服务器代码使用不同的编译配置选项,通过这种方式就不需要在每个文件夹下创建.babelrc 文件了。
Babel 团队做了很多变更来优化代码,并接受了来自 V8 团队的一些补丁(https://twitter.com/rauchg/status/924349334346276864)。
Babel 支持预设和插件选项有一段时间了。比如,你可以将插件包装在一个数组中,并将选项对象传给插件:
// 减号表示移除,加号表示增加:
{
"plugins": [
- "pluginA",
+ ["pluginA", {
+ // options here
+ }],
]
}
Babel 团队还对部分插件的 loose 选项做了一些修改,并为部分插件添加了一些新选项!
比如,对于类 class A {},现在不包含 _classCallCheck。
class A {}
-------------------
// 减号表示移除:
var A = function A() {
- _classCallCheck(this, A);
};
如果 for-of 循环的是一个数组,那么可以使用这个新选项:[“transform-for-of”,{“assumeArray”:true}]
let elm;
for (elm of array) {
console.log(elm);
}
----------------------
let elm;
for (let _i = 0, _array = array; _i < _array.length; _i++) {
elm = _array[_i];
console.log(elm);
}
在 loose 模式下将 transform-typeof-symbol 插件排除在外。
转换后的 ES6 类使用/*#__PURE__*/进行注解,这样就可以告诉 Uglify 和 babel-minify 移除死代码。这些注解也被添加到其他辅助函数中。
class C {
m() {}
}
---------------------
var C =
/*#__PURE__*/
function () {
// ...
}();
以下是 Babel 支持的一些新语法以及已经添加到 v7 中的语法清单:
Babel 团队与 TypeScript 团队合作,让 Babel 使用 @babel/preset-typescript 解析 / 转换类型语法,类似于使用 @babel/preset-flow 处理 Flow 的方式。
之前(有类型):
interface Person {
firstName: string;
lastName: string;
}
function greeter(person : Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
之后(移除了类型):
function greeter(person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
正如 React 博客中所提到的,从 Beta.31 开始,已经支持 JSX Fragment。
render() {
return (
<>
<ChildA />
<ChildB />
</>
);
}
-----------------------
// output
render() {
return React.createElement(
React.Fragment,
null,
React.createElement(ChildA, null),
React.createElement(ChildB, null)
);
}
@babel/runtime 已经被分为 @babel/runtime 和 @babel/runtime-corejs2。前者仅包含 Babel 的辅助函数,后者包含辅助函数以及 polyfill 函数(例如 Symbol、Promise)。
规范规定了需要通过 new Person() 实例化一个类,但如果它被编译成一个函数,就可以直接调用 Person(),所以他们提供了一个运行时检查。
class Person {}
------------------------
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Person = function Person() {
_classCallCheck(this, Person);
};
使用 @babel/plugin-transform-runtime 和 @babel/runtime(作为依赖项),Babel 可以提取单个函数,而且只需要模块函数来获得较小的输出,如下所示:
var _classCallCheck = require("@babel/runtime/helpers/classCallCheck");
var Person = function Person() {
_classCallCheck(this, Person);
};
要使用 polyfill,只需导入 @babel/polyfill 即可:
import "@babel/polyfill";
但这样会导入整个 polyfill,如果浏览器已经支持某些特性,就不需要导入所有内容。可以使用选项 useBuiltins:”entry”来导入需要用到的部分。
可以更进一步,通过选项 useBuiltIns:”usage”只将需要用到的 polyfill 导入到代码库中。
它将遍历每个文件,并在每个使用了内置 polyfill 的文件的顶部注入一个导入语句。例如:
import "core-js/modules/es6.promise";
var a = new Promise();
但它的推理不是很完美,可能会出现误报:
import "core-js/modules/es7.array.includes";
a.includes // assume a is an []
Babel 的一大特点是它的可插拔性。多年来,Babel 从一个“6to5”编译器转变为代码转换平台,为用户和开发人员提供了出色的优化选项。人们已经为特定库和用例开发了大量的 Babel 插件,用以提升库 API 的性能和功能。
可惜的是,将这些插件添加到代码库中需要更改配置,从而增加了代码复杂性。由 Kent C. Dodds 开发的 babel-plugin-macros(https://github.com/kentcdodds/babel-plugin-macros)已经解决了这个!
在安装了 babel-plugin-macros 并将其添加到配置中(它已包含在 create-react-app v2 中)之后,你就不必费心配置就可以使用宏了。此外,为特定应用程序或代码编写自定义转换也变得更加容易。
Babel 一直试图在转换的影响范围和功能之间做出平衡。在 Babel 7 中,通过配置 Babel 来支持模块 / 非模块模式变得更加容易。
值得注意的是,一些流行的 Web 框架的 CLI 工具已经在利用这些支持,这让转换后的 JavaScript 代码减少了大约 20%。
Babel 总是会警告说它不支持扩展原生内置元素(Array、Error 等),但现在可以了!Babel 团队对类插件进行了修改,如果你使用了 preset-env,那么默认就自动启用了这个特性。
Babel 网站从 Jekyll(https://jekyllrb.com/)搬到了 Docusaurus(https://docusaurus.io/)。
Babel 将 REPL 重写为 React 组件,并更好地与 CodeSandbox 集成。这样你就可以在 REPL 中安装来自 npm 的任何插件或预设,并获取 CodeSandbox 的任何更新。
Babel 本质上就是要与 JavaScript 紧紧联系在一起,包括在语法变得“稳定”之前花时间和精力来实现和维护语法。Babel 团队关心的是整个过程:升级路径、新特性的传播、标准 / 语言设计的教学、易用性以及与其他项目的集成。
在 Nicolò的帮助下,Babel 团队几乎完成了新的装饰器的实现。这是一个漫长的旅程,因为新的提案完全不同,而且比旧的更强大。它可能会在下一个次要版本中发布。
还有很多新特性还在开发当中:插件顺序、更好的验证 / 错误、速度提升、新的 loose/spec 选项、缓存、异步使用 Babel、从 CI 构建、冒烟测试、运行 test262。
领取专属 10元无门槛券
私享最新 技术干货