babel知识点梳理

image.png

Babel能做什么?

用于将 ECMAScript 2015+ 代码转换为当前和旧浏览器或环境中向后兼容的 JavaScript 版本,功能主要包括以下:

  • 转换语法
  • 添加目标浏览器中缺少的Polyfill功能
  • 源代码转换

Presets

Babelpreset可以被看作是一组 Babel 插件的集合。 官方预设:

Presets的使用,使用已存在的node_modules中的npm包,或者自定义的文件程序。

{ 
    "presets": ["@babel/preset-env", "./myProject/myPreset"] 
}
复制代码

Stage-X:从 Babel 7 开始, Stage-X 已经被弃用,故不在做其他描述。

执行顺序

presets的执行顺序是从后向前。

{ "presets": ["a", "b", "c"] }
复制代码

先执行c,再b,最后a

Plugins

通过plugins进行代码转换。 pluginspresets一样,也是使用已存在的node_modules中的npm包,或者自定义的文件程序。

plugins执行

  • pluginspresets之前运行
  • plugins从前向后执行,和presets相反

Babel配置文件

  1. 如果你正在使用monorepo或想编译 node_modules,用babel.config.json

  2. 如果是项目的单个部分,用.babelrc.json

  3. 还可以在package.json 中添加Babel的配置

{
    "name": "my-package",
    "version": "1.0.0",
    "babel": {
      "presets": [ ... ],
      "plugins": [ ... ],
    }
}
复制代码

推荐使用babel.config.json

Babel上手

@babel/corebabel的核心功能库,@babel/cli 是一个允许你从终端使用 babel 的工具。

npm install --save-dev @babel/core @babel/cli
复制代码

配置执行转化命令:

"scripts": {
    "build": "babel src --out-dir lib"
}
复制代码

注意:babel转换是依赖插件的,如果没有配置插件的,输出代码将与输入相同。

例如:通过@babel/plugin-transform-arrow-functions可以将 ES2015 箭头函数编译为 ES5

npm install --save-dev @babel/plugin-transform-arrow-functions
复制代码

配置文件:

// babel.config.json
{
  "plugins": ["@babel/plugin-transform-arrow-functions"]
}
复制代码

npm run build后,编译效果如下:

// 编译前:
const fun = () => {
  console.log('fun')
}

// 编译后:
const fun = function () {
  console.log('fun');
};
复制代码

但我们的代码中还有其他 ES2015+ 特性需要转换。我们可以使用一个预设插件(@babel/preset-env)。它只是一组预先确定的插件,不用在一个一个添加所需插件。

此预设是包含所有支持现代 JavaScript(ES2015、ES2016 等)的插件。

Polyfill

Babel 7.4.0 开始,该包已被弃用。取而代之的是直接包含 core-js/stable(用于填充 ECMAScript 功能)和 regenerator-runtime/runtime(需要使用转译的生成器函数)。

@babel/polyfill 模块包括 core-js 和一个自定义的 regenerator 运行时,用来模拟一个完整的 ES2015+ 环境。

有了Polyfill,我们才能使用新的内置函数(如 PromiseWeakMap)、静态方法(如 Array.fromObject.assign)、实例方法(如 Array.prototype.includes)和生成器函数(与 regenerator 插件一起使用时)。

关于添加Polyfill的方式如下:

设置useBuiltInsusageBabel 现在将检查您的所有代码以查找目标环境中缺少的功能,并且仅加载仅包含所需的 polyfill

配置文件:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1"
        },
        "useBuiltIns": "usage"
      }
    ]
  ]
}
复制代码

npm run build后,编译效果如下:

// 编译前:
Promise.resolve().finally();

// 编译后:
require("core-js/modules/es7.promise.finally.js");
Promise.resolve().finally();
复制代码

设置useBuiltInsentry

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1"
        },
        "useBuiltIns": "entry"
      }
    ]
  ]
}
复制代码

然后在我们的入口文件中导入 core-js(以填充 ECMAScript 功能)和 regenerator 运行时(仅当您正在编译生成器时才需要)来模拟完整的 ES2015+ 环境。注意: Babel 7.4.0开始已被弃用@babel/polyfill

import "core-js/stable";
import "regenerator-runtime/runtime";
复制代码

减少体积和避免全局污染

Babel 会使用例如 _extend的功能。结果就是会将此添加到需要它的每个文件中,项目中的多个文件都引用了,就造成了重复。或者如果你不需要像 Array.prototype.includes 这样的实例方法。

那么,就用到@babel/plugin-transform-runtime这个插件了,它的作用如下:

  • 可以重用 Babel 的注入帮助代码以节省代码大小。此插件将引用模块 @babel/runtime 以避免编译输出的重复。
  • 创建一个沙盒环境,避免污染全局作用域
npm install --save-dev @babel/plugin-transform-runtime

npm install --save @babel/runtime // 作为生产依赖
复制代码

配置文件:

{ 
    "plugins": ["@babel/plugin-transform-runtime"] 
}
复制代码

作用1:

编译前:

class Point {
}
复制代码

编译后(没使用@babel/plugin-transform-runtime插件,代码没有重用):

require("core-js/modules/es6.object.define-property.js");

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; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Point = /*#__PURE__*/_createClass(function Point() {
  _classCallCheck(this, Point);
});
复制代码

编译后(使用了@babel/plugin-transform-runtime插件,代码重用):

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var Point = /*#__PURE__*/(0, _createClass2["default"])(function Point() {
  (0, _classCallCheck2["default"])(this, Point);
});
复制代码

比较两次结果,可以看到代码的公用:由直接命名创建函数变为了外部引入。这会有效减少代码体积。

作用2:

编译前:

function* foo() {
  console.log('hello')
}
复制代码

编译后(没使用@babel/plugin-transform-runtime插件,有作用域污染):

var _marked = /*#__PURE__*/regeneratorRuntime.mark(foo);

function foo() {
  return regeneratorRuntime.wrap(function foo$(_context) {
    while (1) {
      switch (_context.prev = _context.next) {
        case 0:
          console.log('hello');

        case 1:
        case "end":
          return _context.stop();
      }
    }
  }, _marked);
}
复制代码

编译后(使用了@babel/plugin-transform-runtime插件,没有作用域污染):

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));

var _marked = /*#__PURE__*/_regenerator["default"].mark(foo);

function foo() {
  return _regenerator["default"].wrap(function foo$(_context) {
    while (1) {
      switch (_context.prev = _context.next) {
        case 0:
          console.log('hello');

        case 1:
        case "end":
          return _context.stop();
      }
    }
  }, _marked);
}
复制代码

@babel/plugin-transform-runtime插件会将这些内置插件重命名为 core-js,而无需再使用 polyfill

Guess you like

Origin juejin.im/post/7069950937388810253