首先要明确的一点是,什么是 polyfill
? 为什么需要 polyfill
?
定义
polyfill
意为腻子。它负责抹平不同环境下的API差异。
这里有一篇之前更新的文章来介绍它。
babel
本身只能转换已经存在的语法,譬如可以将箭头函数转换成普通函数、将class
转换成构造函数。
但它不能转换新语法,譬如 Promise
、includes
、map
等,这些指的都是在全局或者Object
、Array
等的原型上新增的方法。
@babel/polyfill
该库本来是 babel
提供 polyfill
的独立库。
从 babel@7.4.0
开始,推荐直接引入这俩库来代替 @babel/polyfill
。
import 'core-js/stable'
import 'regenerator-runtime/runtime'
@babel/polyfill
的使用
原始使用
直接在入口文件中引入全量包:
import '@babel/polyfill'
结合 @babel/preset-env
使用
babel
不推荐直接在入口文件当中引入 @babel/polyfill
。因为这种方式会引入全量包,导致一些不需要的 polyfill
也会加载进去,增大了包的体积。 因此,往往结合 preset-env
使用 polyfill
。
- 当设置
useBuiltIns
为false
,或者不设置useBuiltIns
选项。需要利用webpack
的entry
属性,将其设置为数组形式:
// webpack.config.js
module.exports = {
entry: ['@babel/polyfill', './src/main.js']
}
- 当设置
useBuiltIns
为'entry'
,根据配置项corejs
的值,具体配置也是不同的。'entry'
意为入口,babel
会在入口处寻找polyfill
并导入全量包。
// 当 corejs:2 时,入口文件main.js引入
import '@babel/polyfill'
// 额外安装 yarn add core-js@2
// 当 corejs:3 时,入口文件main.js引入
import "core-js/stable"
import "regenerator-runtime/runtime"
// 额外安装 yarn add core-js@3
- 当设置
useBuiltIns
为'usage'
,polyfill
会自动被按需引入,只会加载用到的polyfill
, 但要注意的是仍需安装@babel/polyfill
包。只是不需要手动配置而已。
TIP
由于 babel
逐渐弃用 polyfill
,所以在设置 useBuiltIns
时,有可能会遇见错误:
WARNING: We noticed you're using the `useBuiltIns` option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the `corejs` option.
You should also be sure that the version you pass to the `corejs` option matches the version specified in your `package.json`'s `dependencies` section. If it doesn't, you need to run one of the following commands:
npm install --save core-js@2 npm install --save core-js@3
yarn add core-js@2 yarn add core-js@3
显而易见,babel
推荐使用 core-js
。所以解决报错方法是安装 core-js
并在 preset-env
声明其使用版本。
@babel/polyfill
的废弃
@babel/polyfill
被废弃的原因有两个:
- 每个转译的文件都可能会生成大量重复的
helper
工具函数,代码冗余,包体积增大。 @babel-polyfill
修改全局变量。不利于第三方公共库的使用。
我们来看下使用 preset-env
和 @babel/polyfill
转译后的代码。摘抄了下核心部分:
"use strict";
require("@babel/polyfill");
var _this = void 0;
// ①文件顶部存在大量的 helper 工具函数
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
// ②这里是 async 的转译代码。可以发现凭空多了 regeneratorRuntime 这个变量。
function _fn() {
_fn = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
var result;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.prev = 0;
_context.next = 3;
return promise;
case 3:
result = _context.sent;
console.log(result);
_context.next = 10;
break;
case 7:
_context.prev = 7;
_context.t0 = _context["catch"](0);
console.warn('error', _context.t0);
case 10:
console.log('--- after promise ---');
case 11:
case "end":
return _context.stop();
}
}
}, _callee, null, [[0, 7]]);
}));
return _fn.apply(this, arguments);
}
从上述代码可以发现两部分问题:
_interopRequireDefault
、asyncGeneratorStep
、_asyncToGenerator
、_classCallCheck
、_defineProperties
、_createClass
是由preset-env
生成的helper
工具类函数。如果有多个babel
转译的文件,这些文件中都会存在这些函数。多了一个全局变量
regeneratorRuntime
。@babel/polyfill
的引入会暴露这个全局变量以保证代码的正常运行。否则运行代码,会报错regeneratorRuntime is not defined
。
这两部分问题的解决是依赖于@babel/plugin-transform-runtime
。它会把上面的 helper
工具函数以及 regeneratorRuntime
统一从 @babel/runtime
这个库中导入。