babel7
babel7发布了。
在升级到 Babel 7 时需要注意几个重大变化:
- 移除对 Node.js 6 之前版本的支持;
- 使用带有作用域的 @babel 命名空间,以防止与官方 Babel 包混淆;
- 移除年度预设,替换为 @babel/preset-env;
- 使用选择性 TC39 个别提案替换阶段提案;
- TC39 提议插件现在是 -proposal,而不是 -transform;
- 为某些面向用户的包(例如 babel-loader、@babel/cli 等)在 @babel/core 中引入peerDependency。
官方提供了一个工具babel-upgrade,对于老项目,只需要执行:
npx babel-upgrade --write --install
具体看https://github.com/babel/babel-upgrade
useBuiltIns:usage
babel的polyfill总是比较大,会影响一些性能,而且也会有一些没用的polyfill,怎么减少polyfill的大小呢?
babel7提供了useBuiltIns的按需加载:usage。
配置中设置useBuiltIns:usage,babel就会自动把所需的polyfill加载进来,不需要手动import polyfill文件。
配置如:
{
"presets": [
"@babel/preset-react",
["@babel/env", {
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
},
"useBuiltIns": "usage",
"debug": true
}]
],
"plugins": ["@babel/transform-runtime"]
}
babel提供的@babel/env全面替换es2015,stage插件。(如果用到stage的某些插件需要自行引入。个人感觉stage用起来太不方便了)。
之前的babel-preset-env/babel-preset-react全都改名为@babel/xxx,如果在babel7你还按之前的写法,会报错:Error: Plugin/Preset files are not allowed to export objects, only functions.
效果
看下useBuiltIns:usage的效果。"debug"选项开到true,可以看到打包的文件。
我用es6摘抄了一些语法,用来测试编译:
const a = Object.assign({}, { a: 1 });
console.log(a);
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}
let s = Symbol();
typeof s;
class ColorPoint {
constructor(x, y, color) {
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}
asyncPrint('hello world', 50);
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
console.log(hw.next());
babel编译之后,可以看到加载的polyfill只加载了 es6.object.assign,es6.promise, es6.symbol,es7.symbol.async-iterator , regenerator-runtime。
babel是怎么知道我们需要哪些polyfill的?
根据我们填的"targets",babel会去查用到的api,当前的target环境支持什么不支持什么,不支持的才加polyfill。
可以看到我们编译后的文件已经加了polyfill。
文件大小和性能都有很多提高。
useBuiltIns:entry
useBuiltIns:entry就没有那么智能了,他会根据target环境加载polyfill,他需要手动import polyfill,不能多次引入。
@babel/preset-env会将把@babel/polyfill根据实际需求打散,只留下必须的。做的只是打散。仅引入有浏览器不支持的polyfill。这样也会提高一些性能,减少编译后的polyfill文件大小。
main.js需要引入polyfill。import '@babel/polyfill';
。
可以看到效果。我只截了部分polyfill依赖。
编译后的文件引入了一堆polyfill。
最佳实践
只用polyfill不是最完美的方案。
polyfill会额外引入一些函数,比如:
因为polyfill没有babel-runtime的helper函数,在编译async
函数的时候,会引入以上的代码asyncGeneratorStep
,_asyncToGenerator
。
如果你每个文件都用到了async,那么冗余的代码将会很大。
babel-runtime
最佳方案就是在用polyfill的同时,再用babel-runtime。
babel-runtime会把asyncGeneratorStep
,_asyncToGenerator
等函数require进来。从而减小冗余。
这得益于babel-runtime的helper函数。
所以最佳的配置是polyfill+babel-runtime。
如果用了react可以加@babel/preset-react。
{
"presets": [
"@babel/preset-react",
["@babel/env", {
"targets": {
"browsers": ["last 2 versions", "ie 11"]
},
"useBuiltIns": "usage"
}]
],
"plugins": ["@babel/transform-runtime"]
}
可以看到,_asyncToGenerator2已被require。