美文网首页
Babel 进阶二

Babel 进阶二

作者: _于曼丽_ | 来源:发表于2022-04-02 19:33 被阅读0次

    参考文档

    @babel/plugin-transform-runtime 插件用来替换内联辅助函数,替换全局 API,引入 regeneratorRuntime 函数。
    @babel/runtime 工具包提供内联辅助函数,引入 regeneratorRuntime 函数。
    @babel/runtime-corejs2 工具包提供内联辅助函数,提供非全局的 core-js2 版本的 API,引入 regeneratorRuntime 函数。
    @babel/runtime-corejs3 工具包提供内联辅助函数,提供非全局的 core-js3 版本的 API,引入 regeneratorRuntime 函数。
    @babel/runtime@babel/runtime-corejs2@babel/runtime-corejs3 这三个工具包是用来配合 @babel/plugin-transform-runtime 插件使用的,@babel/plugin-transform-runtime 插件根据配置会自动调用这三个工具包。
    通常根据需要安装其中一个工具包即可。

    @babel/plugin-transform-runtime

    @babel/plugin-transform-runtime 插件有三个作用:

    1. 替换内联辅助函数
    2. 替换全局 API
    3. 引入 regeneratorRuntime 函数

    替换内联辅助函数

    参考文档

    在 Babel 在使用 @babel/preset-env 进行语法转换的时候(不是补充 API),常常会注入一些辅助函数。例如转换 ES6 的 Class 语法的时候:

    配置文件:

    const presets = ['@babel/preset-env']
    const plugins = []
    
    module.exports = {
      presets,
      plugins
    }
    

    转换前:

    class Person {
      sayname() {
        return 'name'
      }
    }
    var john = new Person()
    console.log(john)
    

    转换后:

    "use strict";
    
    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); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
    
    var Person = /*#__PURE__*/function () {
      function Person() {
        _classCallCheck(this, Person);
      }
    
      _createClass(Person, [{
        key: "sayname",
        value: function sayname() {
          return 'name';
        }
      }]);
    
      return Person;
    }();
    
    var john = new Person();
    console.log(john);
    

    可以看到转换后的代码上面增加了好几个函数声明,这就是注入的函数,我们称之为辅助函数。

    在我们正常的前端工程开发的时候,少则几十个js文件,多则上千个。如果每个文件里都使用了class类语法,那会导致每个转换后的文件上部都会注入这些相同的函数声明。这会导致我们用构建工具打包出来的包非常大。

    我们把这些辅助函数声明都放在一个npm包里,需要使用的时候直接从这个包里引入到我们的文件里。这样即使上千个文件,也会从相同的包里引用这些函数。通过webpack这一类的构建工具打包的时候,我们只会把使用到的npm包里的函数引入一次,这样就做到了复用,减少了体积。

    @babel/runtime/helpers 包提供辅助函数,安装 @babel/preset-env 的时候会自动安装 @babel/runtime,不过在项目开发的时候,我们一般都会再单独安装一遍 @babel/runtime

    我们手动替换掉辅助函数声明,之前文件的代码就变成如下所示:

    "use strict";
    
    var _classCallCheck = require("@babel/runtime/helpers/classCallCheck");
    var _defineProperties = require("@babel/runtime/helpers/defineProperties");
    var _createClass = require("@babel/runtime/helpers/createClass");
    
    var Person = /*#__PURE__*/function () {
      function Person() {
        _classCallCheck(this, Person);
      }
      _createClass(Person, [{
        key: "sayname",
        value: function sayname() {
          return 'name';
        }
      }]);
      return Person;
    }();
    
    var john = new Person();
    console.log(john);
    

    这样就解决了代码复用和最终文件体积大的问题。不过,这么多辅助函数要一个个记住并手动引入太麻烦了,@babel/plugin-transform-runtime 插件就来帮我们解决这个问题。

    @babel/plugin-transform-runtime 有三大作用,其中之一就是自动移除语法转换后内联的辅助函数(inline Babel helpers),使用 @babel/runtime/helpers 里的辅助函数来替代。这样就减少了我们手动引入的麻烦。

    现在我们除了安装 @babel/runtime 包提供辅助函数模块,还要安装 Babel 插件 @babel/plugin-transform-runtime

    npm i -D @babel/plugin-transform-runtime
    npm i @babel/runtime
    

    配置文件:

    const presets = ['@babel/preset-env']
    const plugins = [
      [
        '@babel/plugin-transform-runtime',
        {
          helpers: true
        }
      ]
    ]
    
    module.exports = {
      presets,
      plugins
    }
    

    转换前代码:

    class Person {
      sayname() {
        return 'name'
      }
    }
    var john = new Person()
    console.log(john)
    

    转换后代码:

    "use strict";
    
    var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
    
    var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
    
    var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
    
    var Person = /*#__PURE__*/function () {
      function Person() {
        (0, _classCallCheck2["default"])(this, Person);
      }
    
      (0, _createClass2["default"])(Person, [{
        key: "sayname",
        value: function sayname() {
          return 'name';
        }
      }]);
      return Person;
    }();
    
    var john = new Person();
    console.log(john);
    

    可以看到,Babel 转码之后,@babel/plugin-transform-runtime 插件自动替换掉了所有的内联辅助函数,转为引用 @babel/runtime 包中的辅助函数。这样在通过 Webpack 打包的时候,不管有多少个 js 文件使用了辅助函数,打包之后的代码里只引入一次相应的辅助函数。

    替换全局 API

    参考文档

    通过引入 @babel/polyfill 或者 core-js/stable,可以补齐全局的 API,但是这样会对全局环境产生污染。例如 Promise API,引入 @babel/polyfillcore-js/stable 会重写了 window.Promise 及其原型链。

    如果我们不想在引入 API 的同时污染全局环境,可以配置 @babel/plugin-transform-runtime 插件,它会将全局 API 的引用替换成 @babel/runtime-corejs2 或者 @babel/runtime-corejs3 包中的非全局 API。

    npm i -D @babel/plugin-transform-runtime
    npm install @babel/runtime-corejs2
    # 也可以安装版本 3
    # npm install @babel/runtime-corejs3
    

    配置文件:

    const presets = ['@babel/env']
    const plugins = [
      [
        '@babel/plugin-transform-runtime',
        {
          // 这里的版本号要与 @babel/runtime-corejs2 一致
          corejs: 2
        }
      ]
    ]
    
    module.exports = {
      presets,
      plugins
    }
    

    转换前代码:

    var obj = Promise.resolve();
    

    转换后代码:

    "use strict";
    
    var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
    
    var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
    
    var obj = _promise["default"].resolve();
    

    可以看到转换后的代码并没有定义全局变量 Promise,而是使用 _promise["default"] 代替所有使用 Promise 的地方,这样就不会污染全局环境了。

    引入 regeneratorRuntime 函数

    • 通过 import 'regenerator-runtime' 可以手动引入全局 regeneratorRuntime 函数。
    • 通过 import '@babel/polyfill' 可以手动引入全局 regeneratorRuntime 函数。
    • 通过 @babel/preset-env 的 useBuiltIns 选项设置为 'usage',可以自动按需引入全局 regeneratorRuntime 函数。
    • 通过 @babel/plugin-transform-runtime 插件的 regenerator 选项设置为 true,可以自动按需引入非全局 regeneratorRuntime 函数。

    配置文件:

    const presets = ['@babel/env']
    const plugins = [
      [
        '@babel/plugin-transform-runtime',
        {
          regenerator: true
        }
      ]
    ]
    
    module.exports = {
      presets,
      plugins
    }
    

    转码之前:

    function* gen () {
      yield 100;
    }
    

    转码之后:

    "use strict";
    
    var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
    
    var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
    
    var _marked = /*#__PURE__*/_regenerator["default"].mark(gen);
    
    function gen() {
      return _regenerator["default"].wrap(function gen$(_context) {
        while (1) {
          switch (_context.prev = _context.next) {
            case 0:
              _context.next = 2;
              return 100;
    
            case 2:
            case "end":
              return _context.stop();
          }
        }
      }, _marked);
    }
    

    可以看到,转换后的代码使用 _regenerator["default"] 代替全局函数 regeneratorRuntime

    babel/plugin-transform-runtime 的配置项

    参考文档

    以下是各个配置项的默认值,如果没有设置对应的配置项,则该配置项取默认值。

    const presets = ['@babel/env']
    const plugins = [
      [
        '@babel/plugin-transform-runtime',
        {
          "helpers": true,
          "corejs": false,
          "regenerator": true,
          "useESModules": false,
          "absoluteRuntime": false,
          "version": "7.0.0-beta.0"
        }
      ]
    ]
    
    module.exports = {
      presets,
      plugins
    }
    

    helpers

    该项是用来设置是否要自动引入辅助函数包,默认值为 true。

    • 当 helpers 为 true 的时候,会自动引用 @babel/runtime/helpers 或者 @babel/runtime-corejs2/helpers 或者 @babel/runtime-corejs3/helpers 来替换内联辅助函数。
    • 当 helpers 为 false 的时候,不会替换内联辅助函数。
    • 通常将该参数设置为 true。

    corejs

    该项是用来定义如何处理 ES6 新增的全局 API,默认值为 false。

    • 当 corejs 为 false 的时候,不对全局 API 进行处理。
    • 当 corejs 为 2 的时候,使用 @babel/runtime-corejs2 包来替换全局 API。
    • 当 corejs 为 3 的时候,使用 @babel/runtime-corejs3 包来替换全局 API。
    • 当 corejs 不为 false 的时候,需要手动安装 @babel/runtime-corejs2@babel/runtime-corejs3

    regenerator

    该项是用来定义如何处理 Generator/async 函数的,默认值为 true。

    • 当 regenerator 为 true 的时候,会按需引入非全局的 regeneratorRuntime 函数。
    • 当 regenerator 为 false 的时候,不会引入 regeneratorRuntime 函数。如果代码中需要该函数,则需要通过 import 'regenerator-runtime' 或者 import '@babel/polyfill' 手动引入。
    • 通常将该参数设置为 true。

    useESModules

    该项用来设置是否使用 ES6 的模块化用法,取值是布尔值,默认是fasle。在用 Webpack一类的打包工具的时候,我们可以设置为 true,以便做静态分析。

    absoluteRuntime 和 version

    保持默认值就可以,可以省略不填。

    @babel/runtime

    配合 @babel/plugin-transform-runtime 插件使用的工具包,用来提供辅助函数和 regeneratorRuntime 函数。

    • @babel/runtime/helpers 提供辅助函数
    • @babel/runtime/regenerator 通过引用 regenerator-runtime 包来提供 regeneratorRuntime 函数

    当我们只需要通过 @babel/plugin-transform-runtime 插件来替换内联辅助函数、按需引入 regeneratorRuntime 函数,不需要替换全局 API 的时候,就可以安装 @babel/runtime 包。

    npm i -D @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-runtime
    npm i @babel/runtime
    

    安装 @babel/preset-env 的时候会自动安装 @babel/runtime 包,不过在项目开发的时候,我们一般都会手动安装一遍 @babel/runtime

    Babel 配置文件:

    const presets = ['@babel/preset-env']
    const plugins = [
      [
        '@babel/plugin-transform-runtime',
        {
          helpers: true,
          corejs: false,
          regenerator: true
        }
      ]
    ]
    
    module.exports = {
      presets,
      plugins
    }
    

    @babel/runtime-corejs2

    配合 @babel/plugin-transform-runtime 插件使用的工具包,用来提供辅助函数、regeneratorRuntime 函数、core-js2 版本的非全局 API。

    • @babel/runtime-corejs2/helpers 提供辅助函数
    • @babel/runtime-corejs2/core-js 提供 core-js@2 版本的 API
    • @babel/runtime-corejs2/regenerator 引用 regenerator-runtime 包来提供 regeneratorRuntime 函数

    当我们需要通过 @babel/plugin-transform-runtime 插件来替换内联辅助函数、按需引入 regeneratorRuntime 函数、替换 core-js2 版本的全局 API 的时候,就可以手动安装 @babel/runtime-corejs2

    npm i -D @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-runtime
    npm i @babel/runtime-corejs2
    

    Babel 配置文件:

    const presets = ['@babel/preset-env']
    const plugins = [
      [
        '@babel/plugin-transform-runtime',
        {
          helpers: true,
          corejs: 2,
          regenerator: true
        }
      ]
    ]
    
    module.exports = {
      presets,
      plugins
    }
    

    @babel/runtime-corejs3

    配合 @babel/plugin-transform-runtime 插件使用的工具包,用来提供辅助函数、regeneratorRuntime 函数、core-js3 版本的非全局 API。

    • @babel/runtime-corejs3/helpers 提供辅助函数
    • @babel/runtime-corejs3/core-js@babel/runtime-corejs3/core-js/stable 提供 core-js@3 版本的 API
    • @babel/runtime-corejs3/regenerator 引用 regenerator-runtime 包来提供 regeneratorRuntime 函数

    当我们需要通过 @babel/plugin-transform-runtime 插件来替换内联辅助函数、按需引入 regeneratorRuntime 函数、替换 core-js3 版本的全局 API 的时候,就可以手动安装 @babel/runtime-corejs3

    npm i -D @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-runtime
    npm i @babel/runtime-corejs3
    

    Babel 配置文件:

    const presets = ['@babel/preset-env']
    const plugins = [
      [
        '@babel/plugin-transform-runtime',
        {
          helpers: true,
          corejs: 3,
          regenerator: true
        }
      ]
    ]
    
    module.exports = {
      presets,
      plugins
    }
    

    相关文章

      网友评论

          本文标题:Babel 进阶二

          本文链接:https://www.haomeiwen.com/subject/yqgtsrtx.html