@babel/polyfill 的使用及效果

原文链接: https://my.oschina.net/u/3117745/blog/2353411

众所周知某些浏览器对ES6+的支持程度堪忧,然而迫于产品的压力我们不得不去像其妥协。所以前端的代码中不可避免的会有一些polyfill代码,polyfill的直译是填充材料。我们正是靠polyfill来实现在旧的浏览器上支持一些新的ES6+的JS特性,例如(Promise、async\await、spread语法等等)。实际上这些polyfill的本质就是通过ES5甚至ES3的代码来实现新的特性,以实现在旧的浏览器上能跑新版本的JS。

babel上常用的polyfill莫过于@babel/polyfill了,其底层是通过core-js来实现polyfill,本文就来小谈一下其使用和效果。

首先安装babel全家桶

"dependencies": {
    "@babel/polyfill": "^7.0.0"
 },
 "devDependencies": {
    "@babel/cli": "^7.1.2",
    "@babel/core": "^7.1.2",
    "@babel/preset-env": "^7.1.0",
    "webpack": "^4.23.1",
    "webpack-cli": "^3.1.2"
 }

然后新增babel配置文件babel.config.js

module.exports = (api) => {
  const presets = [
    [
      "@babel/preset-env", {
        "targets": {
          "chrome": "70",
          "ie": "11"
        },
        "useBuiltIns": "usage"
      }
    ]
  ];
  api.cache(false);

  return { presets };
}

注意两个地方,一个是 targets,这里我写了云泥之别的两个浏览器,一个是当前市面上支持JS新特性最多的Chrome 70,一个是IE 11。后续会拿两者打包后的代码作对比。

还有一个选项是useBuiltIns,下文会说。

先看测试文件 index.js

import util from './util';

function a(args) {
  console.log(...args);
  console.log([...arguments])
}

里面使用了ES6的展开运算符,以及利用[...arguments]来将类数组的arguments对象转成数组。

util.js文件内容如下

export default async function() {
  console.log('hello');
}

单纯的输出了一个async方法。

然后我们开始打包,此时useBuiltIns的取值是usage,targets开启了ie 11: dist/index.js

"use strict";

require("core-js/modules/es6.string.iterator");

require("core-js/modules/es6.array.from");

require("core-js/modules/es6.regexp.to-string");

require("core-js/modules/es7.symbol.async-iterator");

require("core-js/modules/es6.symbol");

require("core-js/modules/web.dom.iterable");

var _util = _interopRequireDefault(require("./util"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }

function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }

function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }

function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }

function a(args) {
  var _console;

  (_console = console).log.apply(_console, _toConsumableArray(args));

  console.log(Array.prototype.slice.call(arguments));
}

dist/util.js

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;

require("regenerator-runtime/runtime");

require("core-js/modules/es6.promise");

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 _default() {
  return _ref.apply(this, arguments);
}

function _ref() {
  _ref = _asyncToGenerator(
  /*#__PURE__*/
  regeneratorRuntime.mark(function _callee() {
    return regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            console.log('hello');

          case 1:
          case "end":
            return _context.stop();
        }
      }
    }, _callee, this);
  }));
  return _ref.apply(this, arguments);
}

可以看到两个文件头部都被自动的引入了core-js的一些modules,值得注意的是,并不是引入完整的core-js,而是根据配置的targets环境和当前JS文件使用了哪些特性来决定需要引入哪些modules。为了验证这一点,我们把targets中的ie 11注释掉,现在只打包chrome 70版本,在chrome中,上述两个文件中用到的语法都是支持的,我们来看一下打包结果: dist/index.js

"use strict";

var _util = _interopRequireDefault(require("./util"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function a(args) {
  console.log(...args);
  console.log([...arguments]);
}

dist/util.js

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = _default;

async function _default() {
  console.log('hello');
}

果然和预想的一致,两个文件中,除了代码被转成es5以外,新的语法特性完全没有任何变化,直接原样输出。 所以,上面这种场景是我们经常会用到的polyfill场景。

useBuiltIns为usage虽然好,但是我们也得看一下另外一个配置值entry。这里就直接叙述结果了,使用entry的话,我们必须在入口文件处加import 'polyfill';,否则不会自动引入。 然后babel会在打包时,将当前环境需要的所有的polyfill module全部在入口文件引入,这里相对于前面的usage来说,就可能引入多余的polyfill module。

useBuiltIns 还有一个默认值 false,这个就很好理解,完全手动polyfill,哪个文件需要polyfill,你就自己在那个文件的顶部引入polyfill。

转载于:https://my.oschina.net/u/3117745/blog/2353411

你可能感兴趣的:(@babel/polyfill 的使用及效果)