去年这个时候,曾经写过一篇文章 Polyfill 的前世今生,总结了常见的 polyfill 方案。受限于当时的认知以及 Babel 版本,有很多疏漏,觉得有必要更新一篇目前(2020.03) polyfill 的一些进展。
在我写下上一篇文章之后两周,2019.03.19,Babel@7.4.0
正式发布了。在 Babel@7.4.0
中,提供了对管道运算符、私有方法、TypeScript 3.4 等的支持,同时,polyfill 开始支持 core-js@3
。对于 core-js ,可能有些人还很陌生,根据 npmtrends 的统计, core-js 事实上已经是目前最流行的 polyfill 方案了,@babel/polyfill
就是靠它来转换代码的,只是很多人可能没有留意到其实自己已经在间接使用它了。回想一下执行 npm install
的时候,是不是对 As advertising: the author is looking for a good job -)
似曾相识? 这条广告还引发过技术人员能不能在 npm log 中为自己打广告的争论, npm fund
也是为此而生。
提到这个,顺便想起了前几天在这条 issue 中看到的一件事, core-js 的作者看起来因交通肇事被判处了 1.5 年的监禁,希望他好好改造重新做人 →_→
在 babel@6.x 版本中,我们通常使用 .babelrc
作为 babel 的配置文件,但是在实践中,遇到了很多问题。现在 babel 团队建议使用这两种方式来对 babel 做出配置:
Project-wide configuration
babel.config.json
files, with the different extensionsFile-relative configuration
.babelrc.json
files, with the different extensionspackage.json
files with a ‘babel’ key
更详细的信息可以参考这里: https://babeljs.io/docs/en/config-files
Babel 团队建议使用 .browserslistrc
文件来指定需要兼容到的浏览器,这样该配置可以被 autoprefixer、 eslint
等其他工具共用。
1 | // 示例: |
在之前的版本中,如果我们将 babel 的 useBuiltIns
属性设置为 entry
或 false
,我们需要在代码中手动引入 @babel/polyfill
,现在则只需要引入 regenerator 和 core-js 就可以了。在代码入口文件前引入它们,可以模拟完整的 ES2015+ 环境(不包含 < Stage 4 的提案)。
1 | // before |
而如果我们对构建产物的大小有限制,我们可以继续使用 useBuiltIns: usage
来按需导入所需的 polyfill 内容。
1 | // babel.config.json |
我们曾经提到过,transform-runtime 的方案不支持如 "foobar".includes("foo")
这样的实例方法。在 core-js@3 中,这个问题得到了解决。
1 | npm remove @babel/runtime-corejs2 |
1 | // babel.config.json |
如果我们需要支持 Stage < 4 阶段的提案,可以配置 proposals
属性来实现:
1 | // babel.config.json |
但是,@babel/plugin-transform-runtime
依然存在问题:它不能像 @babel/preset-env
一样指定 target。也就是说,哪怕针对现代浏览器,它依然会注入大量并不需要的内容,从而使得构建产物的体积大大增加。针对这个问题, babel 团队也还在思考中: https://github.com/babel/babel/issues/10008
在 Babel < 7.4.0 的时代,我们可能都听说过,由于 @babel/polyfill
有全局污染,如果我们在开发一个库/框架,我们只能使用 @babel/runtime
来进行部分 polyfill,其余部分需要库的使用者自行通过 @babel/polyfill
来完成。
现在,如果我们对文件大小不是特别敏感,我们可以不用顾虑这个问题了,直接使用 @babel/preset-env
来进行语法转换,剩余部分 @babel/runtime
即可完成。附上一份完整的 babel.config.json
配置供参考:
1 | // babel.config.json |
如果我们的环境对性能有极致要求,我们则需要使用 @babel/preset-env
并设置 useBuiltIns: "usage"
来完成 ES6+ 的转换。而想要避免全局污染,则需手动引入我们代码中使用到的新特性相关的插件,如 @babel/transform-es2015-classes
、 @babel/transform-es2015-arrow-functions
等。附上一份 @babel/preset-env
配置参考:
1 | // babel.config.json |