深入了解Babel

2021-04-30 10:33:47 浏览数 (1)

这个文档涵盖了所有你想知道的关于 Babel 及其相关工具使用的所有内容。

Babel 是一个用于 JavaScript 的通用多用途编译器,使用 Babel 可以使用(或创建)下一代 的JavaScript,以及下一代 JavaScript 工具。 作为一门语言,JavaScript 不断发展,带来了很多新的规范和建议,使用 Babel 可以让你在这些新的规范和建议全面普及之前就提前使用它们。 Babel 通过将最新标准的 JavaScript 代码编译为已经在目前可以工作的代码来实现上一段提到的内容。这个过程被称为 “源代码到源代码” 的编译,这也被成为 “转换”

例如,Babel 可以将最新的 ES2015 的箭头函数语法从:

代码语言:javascript复制
const square = n => n * n;
复制代码

转换成下面的内容:

代码语言:javascript复制
const square = function square(n) {
  return n * n;
};
复制代码

然而,Babel 可以胜任更多的工作,因为Babel 支持语法扩展,例如 React 的 JSX 语法或者静态类型检查的 Flow 语法。 更近一步,在 Babel 中一切皆插件,而每个人都可以充分利用 Babel 的强大能力来创建属于自己的插件。且 Babel 被组织成几个核心的模块,允许用户利用这些模块来构建下一代 JavaScript 工具链。 许多人也是这样去做的,Babel 的生态系统正在茁长的成长。在这本 Babel 手册中,我将讲解 Babel 内建的一些工具以及社区里的一些拥有的工具。

Babel模块介绍

因为 JavaScript 社区没有标准的构建工具,框架或平台等,Babel 官方性与其他所有的主要工具进行了集成。无论是来自 Gulp、Browserify,或者是 Ember、Meteor,亦或是 Webpack 等,无论你的启动工具是什么,Babel 都存在一些官方性的集成。 就本手册而言,我们将介绍设置Babel的内置方法,但是您也可以访问交互式设置页面[1] 以了解所有集成。

babel-cli

Babel的CLI是从命令行使用Babel编译文件的简单方法。 让我们首先在全局安装它以学习基础知识。

代码语言:javascript复制
npm install --global babel-cli

我们可以像这样编译我们的第一个文件:

代码语言:javascript复制
 babel my-file.js

这会将编译后的输出直接转储到您的终端中。要将其写入文件,我们将指定 --out-file 或 -o

代码语言:javascript复制
 babel example.js --out-file compiled.js
 #or
 babel example.js -o compiled.js

如果我们想将整个目录编译成一个新目录,可以使用 --out-dir 或 -d 来完成。

代码语言:javascript复制
babel src --out-dir lib
#or
babel src -d lib
从项目中运行Babel CLI

虽然您可以在计算机上全局安装Babel CLI,但最好逐个项目在本地安装它。 这有两个主要原因。

  • 同一台计算机上的不同项目可能取决于Babel的不同版本,从而允许您一次更新一个版本。
  • 这意味着您对工作的环境没有隐式依赖。使您的项目更加可移植且易于设置。

我们可以通过运行以下命令在本地安装Babel CLI:

代码语言:javascript复制
npm install --save-dev babel-cli

注意:由于在全局范围内运行 Babel 通常是一个坏主意,因此您可能需要通过运行以下命令来卸载全局副本:

代码语言:javascript复制
npm uninstall --global babel-cli

完成安装后,您的 package.json 文件应如下所示:

代码语言:javascript复制
{
  "name": "my-project",
  "version": "1.0.0",
  "devDependencies": {
    "babel-cli": "^6.0.0"
  }
}

现在,与其直接从命令行运行 Babel,不如将命令放入使用本地版本的 npm 脚本中。只需在您的 package.json 中添加一个 “script” 字段,然后将 babel 命令放入其中即可进行构建。

代码语言:javascript复制
{
    "name": "my-project",
    "version": "1.0.0",
    "scripts": {
      "build": "babel src -d lib"
    },
    "devDependencies": {
      "babel-cli": "^6.0.0"
    }
  }

现在,从我们的终端我们可以运行:

代码语言:javascript复制
npm run build

这将以与以前相同的方式运行Babel,只是现在我们正在使用本地副本。

babel-register

运行Babel的下一个最常见的方法是通过 babel-register 。通过此选项,您仅需要文件即可运行 Babel,这可能会更好地与您的设置集成。

请注意,这并非供生产使用。部署以这种方式编译的代码被认为是不好的做法。最好在部署之前提前进行编译。但是,这对于构建脚本或您在本地运行的其他事情非常有效。 首先让我们在项目中创建一个 index.js 文件。

代码语言:javascript复制
console.log("Hello world!");

如果我们使用 node index.js 来运行它,那么 Babel 不会编译它。因此,我们需要先设置 babel-register 。 首先安装 babel-register

代码语言:javascript复制
npm install --save-dev babel-register

接下来,在项目中创建一个 register.js 文件,并编写以下代码:

代码语言:javascript复制
require("babel-register");
require("./index.js");

这是在 Node 的模块系统中注册 Babel 并开始编译每个 require 的文件。 现在,我们可以使用 node egister.js 代替运行 node index.js 。

代码语言:javascript复制
 node register.js

注意:您不能在要编译的文件中注册 Babel。在 Babel 有机会编译文件之前,Node 正在执行文件。

代码语言:javascript复制
require("babel-register");
// not compiled:
console.log("Hello world!");
babel-node

如果您只是通过 node CLI 运行某些代码,则集成 Babel 的最简单方法可能是使用 babel-node CLI,这在很大程度上只是对 node CLI 的替代。 请注意,这并非供生产使用。部署以这种方式编译的代码被认为是不好的做法。最好在部署之前提前进行编译。但是,这对于构建脚本或您在本地运行的其他事情非常有效。 首先,请确保您已安装 babel-cli 。

代码语言:javascript复制
npm install --save-dev babel-cli

**注意:**如果您想知道为什么要在本地安装此软件,请在上面的项目部分中阅读 “从项目中运行 Babel CLI”。 然后,将运行 node 的任何位置替换为 babel-node 。 如果您使用的是 npm script ,则只需执行以下操作:

代码语言:javascript复制
{
    "scripts": {
-     "script-name": "node script.js"
      "script-name": "babel-node script.js"
    }
  }

否则,您将需要写出通向 babel-node 本身的路径。

代码语言:javascript复制
- node script.js
  ./node_modules/.bin/babel-node script.js
babel-core

如果出于某种原因需要在代码中使用 Babel,则可以使用 babel-core 软件包本身。 首先安装 babel-core 。

代码语言:javascript复制
npm install babel-core
代码语言:javascript复制
var babel = require("babel-core");

如果您具有 JavaScript 字符串,则可以直接使用 babel.transform 对其进行编译。

代码语言:javascript复制
babel.transform("code();", options);
// => { code, map, ast }

如果使用文件,则可以使用异步api:

代码语言:javascript复制
babel.transformFile("filename.js", options, function(err, result) {
  result; // => { code, map, ast }
});

如果您出于任何原因已经拥有Babel AST,则可以直接从AST转换。

代码语言:javascript复制
babel.transformFromAst(ast, code, options);

对于上述所有方法, options 可以传递指南可以从这里了解 babeljs.io/docs/usage/…

配置 Babel

您现在可能已经注意到,仅运行 Babel 似乎除了将 JavaScript 文件从一个位置复制到另一个位置之外没有执行任何其他操作。 这是因为我们尚未告诉 Babel 该做什么事情。

由于Babel是通用编译器,它以多种不同的方式使用,因此默认情况下它不会执行任何操作。您必须明确告诉Babel 它应该做什么。

您可以通过安装 pluginspresets (plugins 组)为Babel提供操作说明。

.babelrc

在我们开始告诉 Babel 怎么做之前。我们需要创建一个配置文件。您需要做的就是在项目的根目录下创建一个 .babelrc 文件。从这样开始:

代码语言:javascript复制
{
  "presets": [],
  "plugins": []
}

该文件是您配置 Babel 以执行所需操作的方式。

注意:虽然您还可以通过其他方式将选项传递给 Babel,但 .babelrc 文件是约定俗成的,也是最好的方法。

babel-preset-es2015

让我们首先告诉 Babel 将 ES2015(JavaScript标准的最新版本,也称为ES6)编译为ES5(当今大多数JavaScript环境中可用的版本)。 我们将通过安装“ es2015” Babel预设来做到这一点(当然目前浏览器支持了绝大部分 ES2015 的特性了,这里是用作演示,使用形式是一致的):

代码语言:javascript复制
 npm install --save-dev babel-preset-es2015

接下来,我们将修改 .babelrc 以包括该预设。

代码语言:javascript复制
  {
    "presets": [
      "es2015"
    ],
    "plugins": []
  }

babel-preset-react 设置 React 同样简单。只需安装预设:

代码语言:javascript复制
$ npm install --save-dev babel-preset-react

然后将预设添加到您的 .babelrc 文件中:

代码语言:javascript复制
{
    "presets": [
      "es2015",
      "react"
    ],
    "plugins": []
  }
babel-preset-stage-x

JavaScript还提出了一些建议,这些建议正在通过TC39(ECMAScript标准背后的技术委员会)流程纳入标准。 此过程分为5个阶段(0-4)。随着提案获得更大的吸引力,并更有可能被采纳为标准,它们经历了各个阶段,最终在阶段4被接纳为标准。 这些以babel的形式捆绑为4种不同的预设:

  • babel-preset-stage-0
  • babel-preset-stage-1
  • babel-preset-stage-2
  • babel-preset-stage-3

请注意,没有阶段 4 的 preset ,因为它只是上面的 es2015 预设。 这些预设中的每个预设都需要用于后续阶段的预设。即 babel-preset-stage-1 需要 **babel-preset-stage-2 ** ,而 babel-preset-stage-3 也需要。

安装您感兴趣的 stage 很简单:

代码语言:javascript复制
npm install --save-dev babel-preset-stage-2

然后,您可以将其添加到您的 .babelrc 配置中。

代码语言:javascript复制
{
    "presets": [
      "es2015",
      "react",
      "stage-2"
    ],
    "plugins": []
  }

执行 Babel 生成的代码

目前,您已经使用Babel编译了代码,但这还不是故事的结局。

babel-polyfill

几乎所有未来 JavaScript 语法都可以使用 Babel 进行编译,但 API 并非如此。 例如,以下代码具有需要编译的箭头函数功能:

代码语言:javascript复制
function addAll() {
  return Array.from(arguments).reduce((a, b) => a   b);
}

在编译之后会变成如下这样:

代码语言:javascript复制
function addAll() {
  return Array.from(arguments).reduce(function(a, b) {
    return a   b;
  });
}

但是,由于 Array.from 并非在每个JavaScript环境中都存在,因此在编译之后它仍然无法使用:

代码语言:javascript复制
Uncaught TypeError: Array.from is not a function

为了解决这个问题,我们使用一种叫做 Polyfill[3] 的东西。简而言之,Polyfill 是一段代码,该代码复制当前运行时中不存在的 API,允许您在当前环境可用之前能提前使用 Array.from 等 API。 Babel使用出色的 core-js[4] 作为其polyfill,以及定制的 regenerator[5] 运行时,以使生成器和异步函数正常工作。 要包含 Babel polyfill,请首先使用npm安装它:

代码语言:javascript复制
npm install --save babel-polyfill

然后只需将 polyfill 包含在任何需要它的文件的顶部:

代码语言:javascript复制
import "babel-polyfill";
babel-runtime

为了实现 ECMAScript 规范的详细信息,Babel将使用 “helper” 方法来保持生成的代码干净。 由于这些 “helper” 方法会变得很长,而且它们被添加到每个文件的顶部,因此您可以将它们移动到 require 的单个“运行时”中。

首先安装 babel-plugin-transform-runtimebabel-runtime

代码语言:javascript复制
 npm install --save-dev babel-plugin-transform-runtime
 npm install --save babel-runtime

然后更新您的 .babelrc :

代码语言:javascript复制
{
    "plugins": [
      "transform-runtime",
      "transform-es2015-classes"
    ]
  }

现在,Babel 将如下代码:

代码语言:javascript复制
class Foo {
  method() {}
}

编译成这样:

代码语言:javascript复制
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";

let Foo = function () {
  function Foo() {
    _classCallCheck(this, Foo);
  }

  _createClass(Foo, [{
    key: "method",
    value: function method() {}
  }]);

  return Foo;
}();

而不是将 _classCallCheck_createClass helper 函数放在需要的每个文件中。

配置 Babel(进阶版)

大多数人都可以通过仅使用内置预设来使用 Babel,但是 Babel 所展现的功能远不止于此

手动指定插件

Babel 预设只是预配置插件的集合,如果您想做不同的事情,可以手动指定插件。这几乎与预设完全相同。 首先安装一个插件:

代码语言:javascript复制
npm install --save-dev babel-plugin-transform-es2015-classes

然后将 plugins 字段添加到您的 .babelrc 中。

代码语言:javascript复制
 {
    "plugins": [
      "transform-es2015-classes"
    ]
  }
插件选项

许多插件还具有将其配置为不同行为的 option 。例如,许多 transform 都具有 loose 模式,该模式会放弃某些规范行为,而倾向于使用更简单,性能更高的代码。 要将选项添加到插件,只需进行以下更改:

代码语言:javascript复制
{
    "plugins": [
-     "transform-es2015-classes"
      ["transform-es2015-classes", { "loose": true }]
    ]
  }

根据环境定制 Babel

Babel插件解决了许多不同的任务。其中许多是开发工具,可以帮助您调试代码或与工具集成。还有许多用于优化生产中代码的插件。 因此,通常需要基于环境的 Babel 配置。您可以使用 .babelrc 文件轻松完成此操作。

代码语言:javascript复制
  {
    "presets": ["es2015"],
    "plugins": [],
    "env": {
      "development": {
        "plugins": [...]
      },
      "production": {
        "plugins": [...]
      }
    }
  }

Babel将根据当前环境在 env 内部启用配置。 当前环境将使用 process.env.BABEL_ENV 。当 BABEL_ENV 不可用时,它将回退到 NODE_ENV ,如果不可用,则默认为“ development ”。

构建自己的预设

手动指定插件?插件选项?基于环境的设置?对于所有项目,所有这些配置似乎都需要重复很多次。 因此,我们鼓励社区创建自己的预设。这可以是您整个公司[10]的预设。 创建预设很容易。假设您有以下 .babelrc 文件:

代码语言:javascript复制
{
  "presets": [
    "es2015",
    "react"
  ],
  "plugins": [
    "transform-flow-strip-types"
  ]
}

您需要做的就是按照命名约定 babel-preset-* 创建一个新项目(请对此命名空间负责!),并创建两个文件。 首先,创建一个新的 package.json 文件,该文件具有您的预设所需的 dependencies 关系。

代码语言:javascript复制
{
  "name": "babel-preset-my-awesome-preset",
  "version": "1.0.0",
  "author": "James Kyle ",
  "dependencies": {
    "babel-preset-es2015": "^6.3.13",
    "babel-preset-react": "^6.3.13",
    "babel-plugin-transform-flow-strip-types": "^6.3.15"
  }
}

然后创建一个 index.js 文件,该文件导出 .babelrc 文件的内容,并用 require 调用替换插件/预设字符串。

代码语言:javascript复制
module.exports = {
  presets: [
    require("babel-preset-es2015"),
    require("babel-preset-react")
  ],
  plugins: [
    require("babel-plugin-transform-flow-strip-types")
  ]
};

Babel 和其他工具结合

一旦掌握了 Babel,Babel 便会很直接地进行设置,但是使用其他工具进行设置可能非常困难。但是,我们尝试与其他项目紧密合作,以使体验尽可能轻松。

静态分析工具

较新的标准为语言带来了许多新语法,而静态分析工具才刚刚开始利用它。

Linting

ESLint 是最受欢迎的 Lint 工具之一,因此,我们维护了官方的 babel-eslint[11] 集成。首先安装 eslint 和 babel-eslint 。

代码语言:javascript复制
npm install --save-dev eslint babel-eslint

接下来,在项目中创建或使用现有的 .eslintrc 文件,并将解析器设置为 babel-eslint 。

代码语言:javascript复制
  {
    "parser": "babel-eslint",
    "rules": {
      ...
    }
  }

现在将一个 lint 任务添加到您的 npm package.json 脚本中:

代码语言:javascript复制
  {
    "name": "my-module",
    "scripts": {
      "lint": "eslint my-files.js"
    },
    "devDependencies": {
      "babel-eslint": "...",
      "eslint": "..."
    }
  }

然后只需运行任务即可完成所有设置。

代码语言:javascript复制
npm run lint

0 人点赞