Skip to content

关于 babel #106

@iiuhuy

Description

@iiuhuy

Babel

关于 Babel 介绍:https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/user-handbook.md

Babel 运行方式:

  • 解析:将字符串代码解析为 AST 抽象语法树;
  • 转换:对抽象 AST 语法树进行转换操作;
  • 再生:根据变换后的 AST 生成代码字符串。

工作流可以理解为:

输入字符串 => parser(@babel/parser) => AST => transformer(不同 plugins 转换代码) => AST => @babel/generator => 输出字符串

Babel 核心模块

by Babel's core packages:https://babeljs.io/docs/en/core-packages

babel/core 做什么用?

  • 加载处理配置(Config);
  • 加载插件;
  • 调用 Parser 进行语法解析,生成 AST;
  • 调用 Traverser 遍历 AST,并使用 访问者模式 应用插件对 AST 进行转换;
  • 生成代码,包括 SourceMap 转换和源代码生成。

babel-register 做什么用?

改写了 require 命令,并为它添加了一个钩子(Hook)。使用的时候只需要引入文件就可以运行,但是不适合在正式环境中使用

例如创建 register.js 文件,添加如下代码:

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

就可以使用 register.js 来代替 node index.js 运行。

$ node register.js

babel-cli

Babel 的 CLI 是一种在命令行下使用 Babel 编译文件的简单方法。


Babel 生成的 AST 是根据 Babel AST format,基于 estree 规范。这个规范定义了一个完整的 ecmascript 语法树,jsx、flow、typescript 在 estree 的规范下做了一些特有的扩充。

关于 AST 树的详细定义 Babel 有文档

babel-polyfill

https://babeljs.io/docs/en/babel-polyfill

babel-polyfill 这里肯定离不开一个开源库 core-js,它提供了 ES5、ES6 的 polyfill,包括:

  • Promise
  • Symbol
  • Collections
  • ES proposals
  • 等等...... 全局对象,以及一些定义在全局对象上的方法(如:Object.assign)都不会转码,因为为了正确的语义,babel 只转换语法而不是去增加或修改原有的属性和方法。

处理这类方法的方案则被称为 polyfill。

babel-polyfill 的缺点:

  • 由于 babel-polyfill 打包出来的体积太大,因为它把所有方法都加到了原型链上。这样就会造成一种浪费,不过可以通过单独使用 core-js 的某个类库来解决
  • babel-polyfill 会造成全局污染,给一些类的原型链上做了修改,所以会倾向于使用 babel-plugin-transform-runtime

babel-runtime

babel-runtime 是由 Babel 提供的 polyfill 库,它本身就是由 core-js 与 regenerator-runtime 库组成,除了做简单的合并与映射外,并没有做任何额外的加工。

babel-plugin-transform-runtime

为了解决每个定义方法的文件,重复插入了一段相同的代码,造成的浪费。

比如安装了:

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

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 这类方法放进每一个需要的文件里去了。

一些问题

Q:Babel 只编译语法不编译 API?
A:Babel 是处于构建时,转义后的结果在默认情况下不包括 ES6 对运行时的扩展。内置对象、内置类型的原型扩展(例如 ES6 对 Array、Object)等都不包括在内,如果使用了 babel-runtimebabel-plugin-transform-runtimebabel-polyfill 就间接引入了 core-js 标准库。

Q:regenerator 运行时库
A:用来实现 ES6/ES7 中 generators、yield、async 及 await 等相关的 polyfills。在 babel-runtime 中被引用。

Q:Babel v6 和 Babel v7 的区别?
A:如下

  • 不再支持 Node.js 0.10, 0.12, 4 和 5。可参考:#[5025]#[5041]#[7755]
  • 移除年度预设用法
  • 移除了 @babel/polyfill 中的提议;
  • 包重命名,babylon 现在重命名为 @babel/parser,babel-cli 变成了 @babel/cli;
  • 把 TC39 提议都换成 -propoal,把那些非年度预设的 TC39 插件中的 -transform 都换成了 -propoal,这样可以更好的区分出一个提议是否为 javascript 官方的。例如:
    • @babel/plugin-transform-function-bind 换成了 @babel/plugin-proposal-function-bind
    • @babel/plugin-transform-class-properties 换成了 @babel/plugin-proposal-class-properties
  • 移除包名中的年份,如:
    • @babel/plugin-transform-es2015-classes 换成了 @babel/plugin-transform-classes

详细参考:

Q:Plugin 插件是如何工作的?
A:了解 Babel 工作流就好理解插件是干嘛的了,@babel/core 加 Plugin,Babel 会按照插件定义的顺序来访问应用的方法。

Q:整个体系执行顺序是怎样的?
A:Plugin(从前向后执行) > Preset(从后向前执行),Preset 的逆向顺序主要是为了保证向后兼容。babel 工作流前面提到了。

  • preset 预设可理解为一组插件。

详细可以查看:https://github.com/iiuhuy/learning/blob/master/docs/babel/babel.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions