preset-env, polyfill, plugin-transform-runtime 三者的作用和区别

325 阅读3分钟

babel 是什么

Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

从字面意思看,从字面意思看,从字面意思看,babel 就是一个能将 高阶的 ES2015+ 的语法转化成低阶 ES5 语法的编译器。如果从这句话完成对 babel 的定性,那么就陷入误区。

babel 能干什么

  • 将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法。
  • 通过 Polyfill 方式在目标环境中添加缺失的特性。 babel 的作用才能真正揭示了 babel 是什么:translater + polyfill。

为什么需要 @babel/preset-env

负责语法转换层面的工作。比如,Class, ArrowFunction, async 等。

为什么需要 @babel/polyfill

负责API转换层面的工作。比如,Map, Set, Promise 等。

为什么需要 @babel/plugin-transform-runtime

复用 @babel/runtime/helpers 中的方法,避免代码冗余。@babel/plugin-transform-runtime 是锦上添花,不是雪中送炭。不使用 @babel/plugin-transform-runtime 也可以,但是不优雅。在不使用 @babel/plugin-transform-runtime 的情况下,每一个 js 文件都会声明自己的工具方法,例如为对象定义属性的方法 defineProperty 会地出现任何需要它的文件中。为什么不将 defineProperty 写在一个公共的地方以供所有需要它的文件复用呢?

function _defineProperty(obj, key, value) {
  if (key in obj) {
    Object.defineProperty(obj, key, {
      value: value,
      enumerable: true,
      configurable: true,
      writable: true
    });
  } else {
    obj[key] = value;
  }

  return obj;
}

@babel/plugin-transform-runtime 就能够解决这个问题。@babel/runtime/helpers 中包含大量的公共方法。

Jietu20220115-182613.png

配置实践

必要的依赖

  "dependencies": {
    "@babel/polyfill": "^7.12.1"
  },
  "devDependencies": {
    "@babel/cli": "^7.16.8",
    "@babel/core": "^7.16.7",
    "@babel/plugin-transform-runtime": "^7.16.8",
    "@babel/preset-env": "^7.16.8",
    "core-js": "^3.20.2"
  }

@babel/preset-env 配置

  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "node": "10.8.0"
        },
        "corejs": 3, // core-js 的版本 2 或者 3
        "useBuiltIns": "usage" // 只包含你所需要的 polyfill
      }
    ]
  ],

useBuiltIns 选项需要说明,默认情况下,@babel/preset-env 会将当前环境看成是最低版本的环境。因此会将所有的特性都添加进来。下面就是@babel/polyfill全部的特性。

"use strict";
require("core-js/es6");
require("core-js/fn/array/includes");
require("core-js/fn/array/flat-map");
require("core-js/fn/string/pad-start");
require("core-js/fn/string/pad-end");
require("core-js/fn/string/trim-start");
require("core-js/fn/string/trim-end");
require("core-js/fn/symbol/async-iterator");
require("core-js/fn/object/get-own-property-descriptors");
require("core-js/fn/object/values");
require("core-js/fn/object/entries");
require("core-js/fn/promise/finally");
require("core-js/web");
require("regenerator-runtime/runtime");

从 @babel/polyfill 可以看出,@babel/polyfill 是依赖 core-js 和 regenerator-runtime。从 @babel/polyfill 的 package.json 文件也可以佐证这一点:package.json 的dependencies 选项包含 core-js 和 regenerator-runtime。

这样做虽然可行,但是没有必要。因为不同版本的环境会在不同程度上支持这些特性。最理想的情况是只需要引入缺失的特性就可以。所以需要告知 babel 当前的环境是什么,当前环境的版本是多少。因此,需要配置 tragets。

targets 选项需要特别说明,targets 指明当前的环境。或者是浏览器环境,或者是 NodeJS 环境。不仅要指定环境还要指定环境的版本。node 是环境,10.8.0 是版本。用户可以在 node.green 上查看当前版本的环境缺失的特性。环境的版本越低,缺失的特性越多,需要 @babel/polyfill 弥补的特性越多。

Jietu20220115-181605.png

参考配置

Babel ES2015+ for NodeJS