Babel知识体系浅谈

2,019 阅读3分钟

我个人正在写Node.js的系列学习笔记node-learning-manual,包含了Node.js的基本模块和工程化相关的知识, 如果对你有帮助,请点个start,您的支持是对我最大的鼓励。

前言

前端发展到现在,可谓是混乱至极,已经远远超出我对前端所谓一把jQuery抄起来就是怼,

Webpack, babel, node.js, react, vue各种工程框架,构建工具你不会点好像都不算是个合格的

前端工程师,语法相关的ES6/7总是绕不过Babel,这篇文章就是直接讨论Babel。

本文只要围绕以下几块来说:

  1. Babel编译过程介绍
  2. Babel插件及预设
  3. Babel-register在项目中的使用
  4. babel的polyfill引入机制
  5. babel在前端工程的定位

Babel编译过程介绍

  1. 核心包

    // 暴露babel.transform方法来编译source code
    babel-core
    // 语法字符串解析parser
    babylon
    // 结合plugins遍历AST语法树
    babel-traverse
    // 生成最后的编译字符串
    babel-generator
    
  2. babel编译流程

    input string
    -> babylon parser
    -> AST
    -> babel-traverse  //使用plugins遍历AST语法树
    -> AST
    -> babel-generator
    -> output string
    

Babel是一个JavaScrpit的编译器,从宏观的角度来看,它有三个运行阶段:解析,转化,生成。基本上如果不设置配置文件.babelrc,Babel运行的结果便是 const babel = code => code ,通过读取代码,最后生成一样的代码。Babel的最核心的概念便是插件,通过在配置文件 .babelrc中添加不同的插件基本可以做所有的事情。

Babel插件(plugins)及预设(presets)

首先聊聊插件与预设的关系,官方预设便是由官方评审维护的一系列插件组合。插件与预设的关系便是父子集合的关系。

每年babel都会评估当年的插件,babel-preset-env 取代了 es2015, es2016, es2017 以及最新的代码

Babel-register在项目中的使用

babel-register的设计思想非常厉害,简单的来说就是require hook。也是babel常见的一种使用方法。

这种方法只需要引入文件就可以运行 Babel,或许能更好地融入你的项目设置。

让我们先在项目中创建index.js文件

// index.js
console.log("hello world");

接着需要我们安装babel-register

$ npm install i -D babel-register

接着,在项目中创建 register.js 文件并添加如下代码:

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

然后我们只需要启动 node register.js 便可,通过修改require function,对所有的通过require引入的代码先经过babel编译一遍,再给到runtime执行。

babel的polyfill引入机制

Babel 几乎可以编译所有时新的 JavaScript 语法,但对于 APIs 来说却并非如此。

比方说,下列含有箭头函数的需要编译的代码:

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

最终会变成这样

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

然而,它依然无法随处可用因为不是所有的 JavaScript 环境都支持 Array.from

为了解决这个问题,我们使用一种叫做 Polyfill(代码填充,也可译作兼容性补丁) 的技术。 简单地说,polyfill 即是在当前运行环境中用来复制(意指模拟性的复制,而不是拷贝)尚不存在的原生 api 的代码。 能让你提前使用还不可用的 APIs,Array.from 就是一个例子。

要使用 Babel polyfill,首先用 npm 安装它:

$ npm install --save babel-polyfill

然后只需要在文件顶部导入 polyfill 就可以了:

import "babel-polyfill";

所以我的个人建议是按需引入core-js的模块而不是整个babel-polyfill bundle,来对ES6/7新增的数据对象和方法做polyfill。

babel-runtime引入机制

为了实现 ECMAScript 规范的细节,Babel 会使用“助手”方法来保持生成代码的整洁。

由于这些助手方法可能会特别长并且会被添加到每一个文件的顶部,因此你可以把它们统一移动到一个单一的“运行时(runtime)”中去。

通过安装 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 这两个助手方法放进每一个需要的文件里去了。

babel总结

babel的出现让开发者可以自由的采用ES6/7的语法来编写JS项目,极大的丰富了开发 (browser, node) 层面的JS语言特性。

babel的AST parser、polyfill、 register一起完成了babel体系对JS的完备解决方案。

参考资料

babel 知识体系漫游

babel handbook

babel docs

我个人正在写Node.js的系列学习笔记node-learning-manual,包含了Node.js的基本模块和工程化相关的知识, 如果对你有帮助,请点个start,您的支持是对我最大的鼓励。