0%

@babel/plugin-transform-runtime与@babel/polyfill与@babel/preset-env

在当下搭建前端工程时一定避不开babel,对于babel最常用的两个库@babel/polyfill@babel/plugin-transform-runtime,需要搞清楚他们的作用和区别。

@babel/polyfill

@babel/polyfill主要提供需要修改内置api(实例方法)才能达成的功能,譬如:扩展Object.prototype,给上面加上assign方法,就属于修改内置API的范畴。

Babel 7.4.0后,这个包被弃用并被两个子包替代:core-js/stable(提供polyfill)和regenerator-runtime/runtime(提供generatorasync方法,可以用runtime实现)。这里就要说到babel 7后的另一个库:@babel/preset-env

@babel/preset-env

@babel/preset-env根据指定的执行环境提供配置polyfill。之前的stage-X库都已废弃。

@babel/preset-env主要是依赖browserslist来设置target进而判断运行环境,然后根据useBuiltIns的配置来决定如何引入polyfill。

useBuiltIns属性主要有三个值:"usage" | "entry" | false,默认是false。这里建议安装core-js替代@babel/polyfill

1
2
3
4
5
npm install core-js@3 --save

# or

npm install core-js@2 --save

useBuiltIns: false

不自动引入polyfills,也不会转换import "core-js"import "@babel/polyfill"引用为单独的polyfill。

useBuiltIns: 'entry'

处理在项目入口的引入,注意你的整个应用(bundle)只能使用一次import "core-jsimport "regenerator-runtime/runtime"。如果多次引入会报错。推荐创建一个只包含import声明的单入口文件。

in

1
import "core-js";

out(基于不同的环境)

1
2
import "core-js/modules/es.string.pad-start";
import "core-js/modules/es.string.pad-end";

in

1
2
import "core-js/es/array";
import "core-js/proposals/math-extensions";

out(基于不同的环境)

1
2
3
4
5
6
7
8
9
import "core-js/modules/es.array.unscopables.flat";
import "core-js/modules/es.array.unscopables.flat-map";
import "core-js/modules/esnext.math.clamp";
import "core-js/modules/esnext.math.deg-per-rad";
import "core-js/modules/esnext.math.degrees";
import "core-js/modules/esnext.math.fscale";
import "core-js/modules/esnext.math.rad-per-deg";
import "core-js/modules/esnext.math.radians";
import "core-js/modules/esnext.math.scale";

useBuiltIns: 'usage'

在文件需要的位置单独按需引入,可以保证在每个bundler中只引入一份。

in

a.js

1
var a = new Promise();

b.js

1
var b = new Map();

out(如果环境不支持上述语法)

1
2
import "core-js/modules/es.promise";
var a = new Promise();
1
2
import "core-js/modules/es.map";
var b = new Map();

out(如果环境支持上述语法)

1
var a = new Promise();
1
var b = new Map();

tips

理论上useBuiltIns: 'entry'这种由入口文件一股脑把所有pollfill全部引入的方式是不推荐的,而useBuiltIns: 'usage'这种按需引入应该是最优的处理方式,但是要注意一点就是useBuiltIns: 'usage'不会处理第三方依赖包的引入模块,所以如果第三方依赖包使用了高级语法而未处理兼容性的话,可能会出bug。

@babel/plugin-transform-runtime

@babel/plugin-transform-runtime主要提供全局内置对象比如PromiseSetMap,并将全局内置对象抽取成单独的模块,通过模块导入的方式引入,避免了对全局作用域的修改(污染)。

有人会奇怪babel里为什么会有@babel/plugin-transform-runtime@babel/runtime?刚开始是只有@babel/runtime插件,但是@babel/runtime有个问题,在代码中直接引入helper函数,意味着不能共享,造成最终打包出来的文件里有很多重复的helper代码。所以,Babel又开发了@babel/plugin-transform-runtime,这个模块会将我们的代码重写,如将Promise重写成_Promise(举例),然后引入_Promise helper函数。这样就避免了重复打包代码和手动引入模块的痛苦。

总结

当前babel版本最优的使用方式应该是@babel/preset-env搭配@babel/plugin-transform-runtime,能够很好的处理高级语法的兼容性问题。