在当下搭建前端工程时一定避不开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
(提供generator
和async
方法,可以用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 | npm install core-js@3 --save |
useBuiltIns: false
不自动引入polyfills,也不会转换import "core-js"
和import "@babel/polyfill"
引用为单独的polyfill。
useBuiltIns: 'entry'
处理在项目入口的引入,注意你的整个应用(bundle)只能使用一次import "core-js
和import "regenerator-runtime/runtime"
。如果多次引入会报错。推荐创建一个只包含import
声明的单入口文件。
in
1 | import "core-js"; |
out(基于不同的环境)
1 | import "core-js/modules/es.string.pad-start"; |
in
1 | import "core-js/es/array"; |
out(基于不同的环境)
1 | import "core-js/modules/es.array.unscopables.flat"; |
useBuiltIns: 'usage'
在文件需要的位置单独按需引入,可以保证在每个bundler中只引入一份。
in
a.js
1 | var a = new Promise(); |
b.js
1 | var b = new Map(); |
out(如果环境不支持上述语法)
1 | import "core-js/modules/es.promise"; |
1 | import "core-js/modules/es.map"; |
out(如果环境支持上述语法)
1 | var a = new Promise(); |
1 | var b = new Map(); |
tips
理论上useBuiltIns: 'entry'
这种由入口文件一股脑把所有pollfill
全部引入的方式是不推荐的,而useBuiltIns: 'usage'
这种按需引入应该是最优的处理方式,但是要注意一点就是useBuiltIns: 'usage'
不会处理第三方依赖包的引入模块,所以如果第三方依赖包使用了高级语法而未处理兼容性的话,可能会出bug。
corejs
2
,2
或{ version: 2 | 3, proposals: boolean }
,默认是2
这个配置项只有当useBuiltIns: usage
或useBuiltIns: entry
时才有效,指定@babel/preset-env
引入的core-js
版本。
当使用useBuiltIns: entry
时,你可以直接引入指定的polyfill比如import "core-js/proposals/string-replace-all"
当使用useBuiltIns: usage
时,有两种选择:
1、将shippedProposals
设置为’true`
2、使用corejs: { version: 3, proposals: true }
@babel/plugin-transform-runtime
@babel/plugin-transform-runtime
主要提供全局内置对象比如Promise
、Set
、Map
,并将全局内置对象抽取成单独的模块,通过模块导入的方式引入,避免了对全局作用域的修改(污染)。
有人会奇怪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
,能够很好的处理高级语法的兼容性问题。