深入了解Babel

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

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

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

const square = n => n * n;
复制代码

转换成下面的内容:

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编译文件的简单方法。 让我们首先在全局安装它以学习基础知识。

npm install --global babel-cli

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

 babel my-file.js

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

 babel example.js --out-file compiled.js
 #or
 babel example.js -o compiled.js

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

babel src --out-dir lib
#or
babel src -d lib

从项目中运行Babel CLI

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

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

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

npm install --save-dev babel-cli

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

npm uninstall --global babel-cli

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

{
  "name": "my-project",
  "version": "1.0.0",
  "devDependencies": {
    "babel-cli": "^6.0.0"
  }
}

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

{
    "name": "my-project",
    "version": "1.0.0",
+   "scripts": {
+     "build": "babel src -d lib"
+   },
    "devDependencies": {
      "babel-cli": "^6.0.0"
    }
  }

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

npm run build

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

babel-register

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

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

console.log("Hello world!");

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

npm install --save-dev babel-register

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

require("babel-register");
require("./index.js");

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

 node register.js

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

require("babel-register");
// not compiled:
console.log("Hello world!");

babel-node

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

npm install --save-dev babel-cli

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

{
    "scripts": {
-     "script-name": "node script.js"
+     "script-name": "babel-node script.js"
    }
  }

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

- node script.js
+ ./node_modules/.bin/babel-node script.js

babel-core

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

npm install babel-core
var babel = require("babel-core");

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

babel.transform("code();", options);
// => { code, map, ast }

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

babel.transformFile("filename.js", options, function(err, result) {
  result; // => { code, map, ast }
});

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

babel.transformFromAst(ast, code, options);

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

配置 Babel

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

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

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

.babelrc

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

{
  "presets": [],
  "plugins": []
}

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

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

babel-preset-es2015

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

 npm install --save-dev babel-preset-es2015

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

  {
    "presets": [
+     "es2015"
    ],
    "plugins": []
  }

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

$ npm install --save-dev babel-preset-react

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

{
    "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 很简单:

npm install --save-dev babel-preset-stage-2

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

{
    "presets": [
      "es2015",
      "react",
+     "stage-2"
    ],
    "plugins": []
  }

执行 Babel 生成的代码

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

babel-polyfill

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

function addAll() {
  return Array.from(arguments).reduce((a, b) => a + b);
}

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

function addAll() {
  return Array.from(arguments).reduce(function(a, b) {
    return a + b;
  });
}

但是,由于 Array.from 并非在每个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安装它:

npm install --save babel-polyfill

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

import "babel-polyfill";

babel-runtime

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

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

 npm install --save-dev babel-plugin-transform-runtime
 npm install --save babel-runtime

然后更新您的 .babelrc :

{
    "plugins": [
+     "transform-runtime",
      "transform-es2015-classes"
    ]
  }

现在,Babel 将如下代码:

class Foo {
  method() {}
}

编译成这样:

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

npm install --save-dev babel-plugin-transform-es2015-classes

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

 {
+   "plugins": [
+     "transform-es2015-classes"
+   ]
  }

插件选项

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

{
    "plugins": [
-     "transform-es2015-classes"
+     ["transform-es2015-classes", { "loose": true }]
    ]
  }

根据环境定制 Babel

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

  {
    "presets": ["es2015"],
    "plugins": [],
+   "env": {
+     "development": {
+       "plugins": [...]
+     },
+     "production": {
+       "plugins": [...]
+     }
    }
  }

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

构建自己的预设

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

{
  "presets": [
    "es2015",
    "react"
  ],
  "plugins": [
    "transform-flow-strip-types"
  ]
}

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

{
  "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 调用替换插件/预设字符串。

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 。

npm install --save-dev eslint babel-eslint

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

  {
+   "parser": "babel-eslint",
    "rules": {
      ...
    }
  }

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

  {
    "name": "my-module",
    "scripts": {
+     "lint": "eslint my-files.js"
    },
    "devDependencies": {
      "babel-eslint": "...",
      "eslint": "..."
    }
  }

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

npm run lint

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 《深入理解ES6》阅读笔记 --- babel

    在2017年相信ES6已经得到了很大的普及,如果你写过React或者Vue,相信在多年之前就已经体验过ES6的魅力了。言归正传,《深入理解ES6》阅读笔记并不会...

    icepy
  • 深入 Babel 6 loose 模式

    Babel 的 loose 模式将 ES6 代码转译成 ES5 代码,loose 模式是不太忠实于 ES6 语义的。这篇文章解释了它是怎么工作的以及它的优点与缺...

    逆葵
  • 入门babel,我们需要了解些什么

    说实话,我从工作开始就一直在接触babel,然而对于babel并没有一个清晰的认识,只知道babel是用于编译javascript,让开发者能使用超前的ES6+...

    Tusi
  • 深入了解ActiveMQ!

    某一天,系统B的负责人告诉系统A的负责人,现在系统B的SystemBNeed2do(String userId)这个接口不再使用了,让系统A别去调它了。 于是,...

    JAVA日知录
  • 深入了解ViewPager2

    ViewPager2从名字就可以看出来它是ViewPager的升级版,既然是升级版那么它相比ViewPager有哪些新变化呢? 添加依赖,目前ViewPage...

    提莫队长
  • 深入了解ConcurrentHashMap

    在上一篇文章【简单了解系列】从基础的使用来深挖HashMap里,我从最基础的使用中介绍了HashMap,大致是JDK1.7和1.8中底层实现的变化,和介绍了为什...

    SH的全栈笔记
  • 深入了解 Eval

    在严格模式下,eval有自己的词法环境。因此,在eval内部声明的函数和变量在外部不可见:

    公众号---人生代码
  • 深入了解 BigInt

    创建bigint的方法是在整型文字的末尾加上n,或者调用函数bigint从字符串、数字等创建bigint。

    公众号---人生代码
  • 深入了解Webpack

    本质上,有两种构建JavaScript应用程序的模式:开发和生产。以前,您已使用开发模式在本地开发环境中开始使用Webpack Dev

    若尘_
  • 一文带你了解babel-preset-env

    相信很多人都和我一样,刚接触babel的时候都是使用 babel-preset-es2015 这个预设套餐的,但是显然目前而言 babel-preset-env...

    用户1515472
  • 深入了解原型

    说原型之前先说说对象,好像在工作中,对象用的挺多的,原型基本上没有用。既然没有用那我还要不要学习呢?思考了很久,还是学一学,万一以后的工作用的着呢?领导常说,上...

    sunseekers
  • 深入了解SQLMAP API

    以前觉得sqlmap自己玩得挺溜了,结果最近有一个任务,需要调用sqlmap api接口来验证存在sql注入漏洞的站点,一开始听到这个任务觉得完了,可能完成不了...

    FB客服
  • SVN之深入了解

    其实我们所说的SVN就是Subversion的简称,是一个开放源代码的版本控制系统,相较于RCS、CVS,它采用了分支管理系统,它的设计目标就是取代CVS。互联...

    良月柒
  • 深入了解linux inode

    文件是储存在硬盘上的,硬盘最小的存储单位叫做扇区sector,每个扇区存储512个字节。操作系统读取硬盘的时候,不会一个个扇区地读取,而是一次性地读取多个扇区,...

    Steve Wang
  • 深入了解 Dynamic imports

    这是因为import/export的目的是为代码结构提供一个主干。这是一件好事,因为代码结构可以被分析,模块可以被收集并通过特殊工具绑定到一个文件中,未使用的导...

    公众号---人生代码
  • 深入了解Java锁

    继 打印Java对象头,我们再深入探索一下Java锁。无锁状态我们就不说了,下面我们一一打印偏向锁、轻量锁,重量锁的对象头。

    用户5475193
  • 深入了解Webpack 5

    本质上,有两种构建JavaScript应用程序的模式:开发和生产。以前,您已使用开发模式在本地开发环境中开始使用Webpack Dev Server。您可以更改...

    前端小tips
  • Babel 的原理

    完整高频题库仓库地址:https://github.com/hzfe/awesome-interview

    HZFEStudio
  • 由 for...of 深入看 Babel 转码的局限

    ES6 借鉴了其他编程语言的特性,为 JavaScript 带来了 for…of 循环语法,用于遍历数组等数据结构。当然,由于是 ES6 的特性,我们使用 fo...

    逆葵

扫码关注云+社区

领取腾讯云代金券