Babel的内置功能(上)

babel最开始的名字叫做6to5,主要是做es6到es5语法的转换和polyfill,后来在4.0版本时改成了babel。虽然从6to5改名到了babel,但是做的事情却没有变化,依然是将高版本的js语法转化为低版本的语法,并自动polyfill缺少的语法。

babel是如何实现这些功能的呢?

从plugin到preset

要实现转换,第一步要明确转换什么;划定一个集合放要转换的特性,再划定一个集合放转换到的目标特性,两者建立一一映射关系。就确定了我们要做哪些转换。

exponentiation operator

比如幂运算,我们会用Math.pow来实现

js 复制代码
let x = 10 ** 2;

x **= 3;

转换为

js 复制代码
let x = Math.pow(10, 2);

x = Math.pow(x, 3);

class

再比如class,我们会用function、prototype来实现

js 复制代码
class Test {
  constructor(name) {
    this.name = name;
  }

  logger() {
    console.log("Hello", this.name);
  }
}

转换为:

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

var Test = (function() {
  function Test(name) {
    _classCallCheck(this, Test);

    this.name = name;
  }

  Test.prototype.logger = function logger() {
    console.log("Hello", this.name);
  };

  return Test;
})();

每一个语法都可以这样转换为低版本的语法,那把所有的这种高版本语法写的代码转换为低版本的,这就实现了编译。

但是只是转换并不能解决所有问题,涉及到某个对象的api,比如Array.prototype.find,这种api的兼容并不是需要语法转换外,而是需要在环境中注入我们实现的api,也就是polyfill(垫片)。

所以我们做的事情除了语法转换外,还有api的polyfill。

先说语法转换:

我们要转换哪些语法呢?

babel插件需要转换的语法包括es标准语法,还有react、flow、typescript等特有语法。

es 标准语法

我们知道TC39是制定javaascript语言标准的组织,每年都会公布加入到语言标准的特性,es6,es7等等,都是我们要转换的语言特性范围。 在babel6的时候,分别用preset-es2015、preset-es2016等来维护对应的transform plugin,但在babel7的时候就改为了preset-env了。

proposal阶段的语法

babel要转换的不只是加入标准的特性,语法特性从提出到标准会有一个过程,分为几个阶段:

  1. 阶段0:Strawman - 只是一个想法,可能用babel plugin实现。
  2. 阶段1:Proposal - 值得继续的建议
  3. 阶段2:Draft - 建立spec
  4. 阶段3:Condidate - 完成spec并且在浏览器实现
  5. 阶段4:Finished - 会加入到下一年的es20xx spec

这些还未加入到语言标准的特性也是要支持的。

react、flow、typescript

只是转换javascript本身的的es spec和proposal的特性并不够,现在我们开发的时候jsx、typescript、flow这些都是会用的,babel肯定也得支持。

这些转换对应的plugin分别放在不同的preset中:preset-jsx、preset-typescript、preset-flow。

转换的范围又大了些:

上面是插件要转换的语言特性,babel7内置的实现这些特性的插件分别是syntas、transform、proposal 3类。

syntax plugin

syntax plugin是在parserOptions中放入一个flag,让parser知道要parse什么语法,最终parse的逻辑还是babel parser(babylon)实现的。

一般syntax plugin是这样实现的:

js 复制代码
import { declare } from "@babel/helper-plugin-utils";

export default declare(api => {
  api.assertVersion(7);

  return {
    name: "syntax-function-bind",

    manipulateOptions(opts, parserOpts) {
      parserOpts.plugins.push("functionBind");
    },
  };
});

这些插件的目的就是让parser能够正确解析对应的语法成AST。

transform plugin

transform plugin是对 AST 的转换,各种es20xx语言特性、typescript、jsx语言特性等都是在transfrom里实现的。

有的时候需要结合syntax plugin 和 transform plugin,比如typescript的语法解析要使用 @babel/plugin-syntax-typescript 在parseOptions里放入typescript的语法选项,然后使用@babel/plugin-transform-typescript来转换解析出的typescript对应的AST。

平时我们都是使用的@babel/preset-typescript,它对上面的两个插件做了封装,是他们的集合。

proposal plugin

未加入语言标准的特性的 AST 转换插件叫proposal plugin,其实他也是transform plugin,但是为了和标准特性区分,所以才这样叫。

完成proposal特性的支持,有时同样需要syntax plugin 和 proposal plugin,比如function bind(::操作符),就需要同时使用@babel/plugin-syntax-function-bind 和 @babel/plugin-proposal-function-bind。

总之,babel内置的plugin就 @babel/syntax-plugin-xxx、@babel/transform-plugin-xxx、@babel/proposal-plugin-xxx 3种。

这样的plugin还很多,所以又设计了preset。

preset

用于不同的目的,需要不同的babel插件,所以babel设计了preset。

  • 不同版本的语言标准支持设计了:preset-2015、preset-2016等,babel7用preset-env来代替。
  • 未加入标准的语言特性的支持:用于stage0、stage1、stage2的特性,babel7之后单独引入proposal plugin。
  • 用于jsx、react、flow支持:分别封装对应的preset-jsx、preset-react、preset-flow,直接使用对应的preset即可。

preset就是插件的集合,但是它可以动态的确定所包含的插件,比如preset-env就是根据stages来确定插件。

babel runtime

babel runtime里面放运行时加载的模块,会被打包工具打包到产物中,下面放着各种需要在runtime使用的函数, 包括三部分:regenerator、corejs、helper。

  • corejs就是新的 api 的 polyfill,分为2和3两个版本,3才实现了实例方法的polyfill。
  • regenerator是facebook实现的 async 的 runtime库,babel使用regenerator-runtime来支持实现async await的语法。
  • helper 是babel做语法转换时用到的函数,比如_typeof、_extends

babel做语法转换和api的polyfill,需要自己实现一部分runtime的函数,就是helper部分。

有的也没有自己实现,用的第三方,比如regenerator用的facebook的, api的polyfill也是用core-js的,babel对他们做了整合。

因为async await这种特性的实现还是比较复杂的,标准api的实现也需要花精力,所以babel就直接使用了社区的实现。

相关推荐
过云鱼1 个月前
Babel 快速上手
面试·babel·前端工程化
aoi4 个月前
Babel 示例插件:处理 AST 替换变量、箭头函数
javascript·babel
文艺理科生6 个月前
Webpack项目构建入门:babel的核心角色
前端·webpack·babel
阿镇吃橙子7 个月前
由浅入深 ——Vite工具链学习总结
vue.js·vite·babel
总之就是非常可爱7 个月前
提升前端开发效率:利用 Babel 实现 JavaScript 文件的定制化修改
前端·babel
月下点灯7 个月前
小白也能看懂的AST抽象语法树+babel插件开发教程
前端·javascript·babel
let_code8 个月前
Babel
前端·babel
ichimaru_xx8 个月前
node前端工具实战-svg引入整理工具
node.js·babel
每天写一个BUG8 个月前
简单玩一玩 Babel
前端·babel·前端工程化
yuansucai8 个月前
LocatorJS接入
babel