Skip to content

@babel/polyfill 与 @babel/plugin-transform-runtime 详解 #4

Open
@SunshowerC

Description

@SunshowerC
Owner

前言

很多人可能看完 babel 的官方文档 ,依然不是很了解其中的一些特性,这里我详细解读一下,供大家参考参考。

@babel/preset-env

@babel/preset-env 会根据 browserlist 配置进行转换,如果需要兼容比较旧的浏览器,需要手动引入 @babel/polyfill

option

  • targets.esmodules:boolean = false
    请注意:在指定 esmodules 目标时,将忽略 browserlists, 即 useBuiltIn 会失效,不转化 es6 语法也不 polyfill
    如果 想用 esmodules 又需要 polyfill ,请组合使用 modules = false , useBuiltIn

  • useBuiltIns = false
    根据 browserlist 是否转换新语法与 polyfill 新 API

    1. false : 不启用polyfill, 如果 import '@babel/polyfill', 会无视 browserlist 将所有的 polyfill 加载进来
    2. entry : 启用,需要手动 import '@babel/polyfill', 这样会根据 browserlist 过滤出 需要的 polyfill
    3. usage : 不需要手动import '@babel/polyfill'(加上也无妨,构造时会去掉), 且会根据 browserlist + 业务代码使用到的新 API 按需进行 polyfill
  • modules = 'commonjs'
    "amd" | "umd" | "systemjs" | "commonjs" | "cjs" | false, defaults to "commonjs".
    转换 es6 模块语法到其他 模块规范, false不会转换

  • include:Array<string|RegExp> = []
    如果你 使用了某个新特性(如es6.array.from),无论browserslist 如何你都想 转化它, 则 include: ['es6.array.from']

  • exclude:Array<string|RegExp> = []
    同理

  • loose = false(推荐)
    loose mode
    优势:代码更加简洁,更容易看懂,可能被老浏览器引擎执行得更快,兼容性更好。
    缺点:当从 编译后的 es6 代码转换成 原生 es6 代码,有可能出现问题。这不值得冒险启用 loose
    多数的 babel plugin 有两种模式,普通模式会将代码编译成尽可能接近 es6 语义,loose 模式则会将代码编译成 es5 风格。如:

// 源码
class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() {
        return `(${this.x}, ${this.y})`;
    }
}
// 普通模式 更接近 es6
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; }

var Point =
/*#__PURE__*/
function () {
  function Point(x, y) {
    _classCallCheck(this, Point);

    this.x = x;
    this.y = y;
  }

  _createClass(Point, [{
    key: "toString",
    value: function toString() {
      return "(".concat(this.x, ", ").concat(this.y, ")");
    }
  }]);

  return Point;
}();

// loose = true编译模式 更接近 es5
var Point =
/*#__PURE__*/
function () {
  function Point(x, y) {
    this.x = x;
    this.y = y;
  }

  var _proto = Point.prototype;

  _proto.toString = function toString() {
    return "(" + this.x + ", " + this.y + ")";
  };

  return Point;
}();

  • forceAllTransforms = false
    默认情况下, preset-env 会把根据 browserslist 进行有必要的 transform, 但是你可以强制所有 es6 语法都转换,通常用于 应用只支持 es5 的情况下。 此属性不影响 polyfill。

  • configPath = process.cwd()
    .browserslist(或 package.json->browserslist) 配置所在文件夹,根据此文件夹一直向父文件夹查找,直到找到配置文件

  • ignoreBrowserslistConfig = false
    忽略 .browserslist 配置

  • shippedProposals = false
    是否启用 还在提案中但已经被浏览器正式使用的新特性。如果你要支持的浏览器很新,已经支持了某些提案,可以启用这个选项,避免语法转换。 这个属性和 @babel/preset-stage-3 有所区别,stage-3 新特性在还未正式上线浏览器仍然有可能被修改变更的哦

@babel/plugin-transform-runtime

@babel/plugin-transform-runtime 不能单独使用,它需要指定 preset 为 es2015,env, typescript 还是 其他,才知道要转换的特性有哪些

babel 在每个需要的文件的顶部都会插入一些 helpers 代码,这可能会导致多个文件都会有重复的 helpers 代码。
@babel/plugin-transform-runtime + @babel/runtime 可以避免编译构建时重复的 helper 代码

此转换器的另一个目的是为您的代码创建沙盒环境。如果您使用@ babel / polyfill及其提供的内置函数(例如Promise,Set和Map),那些将污染全局范围。虽然这可能适用于应用程序或命令行工具,但如果您的代码是您打算发布供其他人使用的库,或者如果您无法准确控制代码运行的环境,则会出现问题。

适用于不需要修改 全局变量的工具/库,同时,适用这种方法也不会转换实例的方法(如:Array.prototype.includes)

PS: 为什么 transform-runtime 不会转换实例的方法呢?这是因为,前面讲到的transform-runtime是为代码创建沙盒环境,并不会污染全局,假如要转换'abc'.includes(xxx),势必会重写 includes,和 transform-runtime 的初衷相悖。

有人又说了,通过自定义函数transformedIncludes('abc', xxx)不就行咯?要知道,js 是门动态语言,如果存在foo.includes('a'),你根本无法知道这里的 includes 到底是 String.prototype.includes , 还是 Array.prototype.includes,亦或是 自定义对象上的 includes 方法,自然无法 转换

那么,同样的限制,为啥子 @babel/preset-env 就能 polyfill includes 实例方法的呢?其实很简单粗暴,只要有变量出现 includes 方法, @babel/preset-env 会有杀错没放过,把 es6.string.include 和 es7.array.includes 都加载进来。

option

  • corejs:boolean|number = false | 2
    是否转化 内置函数(如:Promise, Set, Symbol) 或者 静态方法(如:Object.assign, Array.from)
  • regenerator:boolean = true (推荐 true)
    默认情况下回根据 browserslist 来确认是否转化 generator 函数 或 async 函数,如果 @babel/preset-env -> ignoreBrowserslistConfig = true 则都转换 generator 和 async 语法。
  • helpers:boolean = true (推荐true)
    是否将内联的 babel helpers 代码抽离到单独的 module 文件,避免内联的 helper 代码在多个文件重复出现。
  • useESModules:boolean = false (推荐 true)
    启用时将会加载 esModules 规范的 helpers 函数代码,这样webpack构建出来的代码会更小,因为这无需保留commonjs语义。
    禁用时代码:
    exports.__esModule = true;
    exports.default = function(instance, Constructor) {
      if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
      }
    };
    
    启用时代码:
    export default function(instance, Constructor) {
      if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
      }
    }
    

Activity

changed the title [-]@babel/polyfill 和 @babel/plugin-transform-runtime 详解[/-] [+]@babel/polyfill 与 @babel/plugin-transform-runtime 详解[/+] on Dec 1, 2018
lushijie

lushijie commented on Mar 8, 2019

@lushijie

@babel/plugin-transform-runtime, userESModules, When enabled, the transform will use helpers that do not get run through @babel/plugin-transform-modules-commonjs.

userESModules: true,
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/createClass"));

lushijie

lushijie commented on Mar 8, 2019

@lushijie

@babel/polyfill useBuiltIns: 'usage' yarn add core-js --save

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @lushijie@SunshowerC

        Issue actions

          @babel/polyfill 与 @babel/plugin-transform-runtime 详解 · Issue #4 · SunshowerC/blog