使用@babel/polyfill可以让你在任何es2015+的环境中编写代码,而不需要担心兼容性问题。它会在全局变量上添加一些类似于原生的方法。但是webpack一直以来配置都特别复杂,直到webpack4才开始做0配置。项目中如果需要webpack的配置可能都是ctrl+c ctrl+v,没有及时去更新,会对polyfill有一些误解,比如说,项目中会同时出现babel-plugin-transform-runtime
和babel-polyfill
,在已经使用了babel-polyfill
的基础上还是会担心使用新语法带来的问题。这篇文章将会解答一些与babel-polyfill
相关的问题。
polyfill与core-js
- 简单来说
polyfill
包括了core-js
和regenerator-runtime
(转义async和await的包)。 -
而且babel7.4.0开始@babel/polyfill改成直接引入
core-js
和regenerator-runtime
了As of Babel 7.4.0, this package has been deprecated in favor of directly including core-js/stable (to polyfill ECMAScript features) and regenerator-runtime/runtime (needed to use transpiled generator functions):
import "core-js/stable"; import "regenerator-runtime/runtime";
polyfill与preset-env
preset-env
中我们需要关注以下两个属性
targets
targets可以设置polyfill引入哪些方法的转义,因为preset-env
有一个内置的json文件,可以根据用户设置过滤所需要polyfill的方法
// corejs2-built-ins.json
{
"es6.array.copy-within": {
"chrome": "45",
"edge": "12",
"firefox": "32",
"safari": "9",
"node": "4",
"ios": "9",
"samsung": "5",
"opera": "32",
"electron": "0.35"
},
"es6.array.every": {
"chrome": "5",
"opera": "10.10",
"edge": "12",
"firefox": "2",
"safari": "3.1",
"node": "0.10",
"ie": "9",
"android": "4",
"ios": "6",
"phantom": "2",
"samsung": "2.1",
"electron": "1.1"
},
……
}
// 根据项目需要兼容的最低版本
{
"targets": {
"chrome": "58",
"ie": "11"
}
}
useBuiltIns
useBuiltIns有两个特别有用的属性,usage和entry,使用两个属性都是直接引用core-js
对应的包。
- entry的用法:项目中
import "@babel/polyfill";
persets的options中使用useBuiltIns: "entry"
,配合targets选项,只import最低版本所需要的方法垫片 -
usage的用法:只需要在
persets的options中使用
useBuiltIns: "usage"即可将所有项目中用到所需要在最低浏览器版本不兼容的方法垫片,例如 includes 方法就会自动
import "core-js/modules/es6.string.includes";`//demo // index.js console.log("test123".includes("test")); // webpack.config.js use: { loader: "babel-loader", options: { presets: [ [ "@babel/preset-env", { targets: { chrome: "58", ie: "11" }, useBuiltIns: "usage" } ] ] } }
-
因为 includes 在 ie11 中不兼容,打包结果:
- 不使用
@babel/preset-env
,打包后文件 _900 多 bytes 不到 1kb_,不能兼容低版本浏览器 - 使用
preset-env
+useBuiltIns: "usage"
,babel 识别到使用了 includes 方法,加入 polyfill 的方法有[ 'es6.string.includes', 'es7.array.includes' ],打包后文件为 6.56kb - 使用
preset-env
+useBuiltIns: "entry"
,并且手动 import@babel/polyfill
(使用 useBuiltIns webpack 都会建议不需要手动 import polyfill,如果它检测到用户已经 import 了会提示 useBuiltIns 选用 entry 属性),polyfill 的几十个方法,也就是按照 targets 的配置将所有需要 polyfill 方法都打包进来,打包后文件为 76.7kb
{ 'es6.array.copy-within', 'es6.array.fill', 'es6.array.find', 'es6.array.find-index', 'es7.array.flat-map', 'es6.array.from', 'es7.array.includes', …… }
- 只使用 babel-loader+
@babel/preset-env
,打包文件大小为 86.3kb - 因此,如果项目本身是有兼容性要求的话,一般都会使用 babel-loader 做兼容,因为很常见的 array.from 也是要做 polyfill 的,加上 preset-env + useBuiltIns 按需加载使用一些新语法其实不需要太担心包大小的问题,也不需要手动 import
@babel/polyfill
- 不使用
- 虽然在babel-preset-env官方文档中看到
usage
还是experiment状态,但是可以在vue-cli的源码看到默认打包的app的presets配置里useBuiltIns是usage
,所以可以放心的用(其实babel6的时候usage
就已经是experiment,babel7也还是)/vue-cli/packages/@vue/babel-preset-app/index.js
- 需要注意的是如果你使用的不是TC39 stage4的提案,你还是需要自己手动去设置
babel-plugin
,preset-env
不会帮你引入其他stage的语法垫片
polyfill 与 plugin-transform-runtime
- 如果有长时间使用过webpack,一定不会对
babel-plugin-transform-runtime
这个插件陌生,大家都有做兼容的环境的作用,甚至有些项目还会看到同时使用babel-polyfill
和babel-plugin-transform-runtime
,这种做法貌似在babel-preset-env
出来之前是有一定道理的。 - 实际上
transform-runtime
的转换是非侵入性的,也就是它不会污染你的原有的方法,比如挂在Array原型上的includes方法就只能使用babel-polyfill
-
transform-runtime
的使用场景应该是库,遇到需要转换的方法它会另起一个名字,否则会直接影响使用库的业务代码,平常的项目使用babel-polyfill
即可。
polyfill.io
- 虽然presets-env+实际压缩已经能够优化掉大部分体积的polyfill,但是对于一些最新浏览器版本来说,任何的polyfill都是浪费资源的。
- 这时候polyfill.io能够解决这个问题。polyfill.io以服务端渲染的方式从请求中获取到useragent的信息,然后返回对应的polyfill。但据说是因为浏览器版本太多,国内还有套壳的360/qq等浏览器,ua要是判断失误没有其他回退的方案,因此没有办法广泛应用起来。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。