Babel

1.Babel简介

Babel是一个多功能的JavaScript编译器,更确切的说是源码到源码的编译器,通常也叫做“转换编译器”, 意思是说你为 Babel 提供一些 JavaScript 代码,Babel 更改这些代码,然后返回给你新生成的代码。
主要用于将ECMAScript 2015+代码转换为当前和旧版浏览器或环境中的向后兼容版本的JavaScript。以下是Babel可以做的主要事情:

  • 转换语法
  • Polyfill功能,目标环境中缺少的(通过@babel/polyfill实现)
  • 源代码转换
// Babel Input: ES2015 arrow function
[1, 2, 3].map((n) => n + 1);

// Babel Output: ES5 equivalent
[1, 2, 3].map(function(n) {
  return n + 1;
});

2.Babel工作原理

分为三个阶段:parsing, transforming, generating。

ES6代码输入 -> babylon进行解析-> 得到AST
-> plugin用babel-traverse对AST树进行遍历转译 -> 得到新的AST树
-> 用babel-generator通过AST树生成ES5代码

整个工作过程都是涉及到AST(抽象语法树).

  1. 解析
    该步骤接收代码并输出 AST。 分为两个阶段:词法分析(Lexical Analysis) 和 语法分析(Syntactic Analysis)。
    词法分析阶段把字符串形式的代码转换为 令牌(tokens) 流,语法分析阶段会把一个令牌流转换成 AST 的形式。
  2. 转换
    转换步骤接收 AST 并对其进行遍历,在此过程中对节点进行添加、更新及移除等操作。 这是 Babel 或是其他编译器中最复杂的过程同时也是插件将要介入工作的部分。
  3. 生成
    代码生成步骤把最终(经过一系列转换之后)的 AST 转换成字符串形式的代码,同时还会创建源码映射(source maps)。

一段js:

function square(n) {
  return n * n;
}

对应的ast:

- FunctionDeclaration:
  - id:
    - Identifier:
      - name: square
  - params [1]
    - Identifier
      - name: n
  - body:
    - BlockStatement
      - body [1]
        - ReturnStatement
          - argument
            - BinaryExpression
              - operator: *
              - left
                - Identifier
                  - name: n
              - right
                - Identifier
                  - name: n

2.Babel包相关介绍

(version 7)版本开始,所有的Babel模块都作为单独的npm包发布在@babel下,模块化的设计允许各种工具对特定用例进行设计。

  • Babel-core: 核心功能在@babel/core下
  • babel-plugin-xxx:babel转译过程中使用到的插件,其中babel-plugin-transform-xxx是transform步骤使用的
  • babel-preset-xxx: transform阶段使用到的一系列的plugin
  • babel-polyfill: JS标准新增的原生对象和API的shim,实现上仅仅是core-js和regenerator-runtime两个包的封装
  • babel-runtime: 功能类似babel-polyfill,一般用于library或plugin中,因为它不会污染全局作用域

工具包:

  • Babel-cli: 允许在命令行中使用babel.
  • Babel-register: 通过绑定node.js的require来自动转译require引用的js代码文件

Plugins & Preset:

转换阶段以插件形式进行,转换ES2015+为ES5可以用官方插件:@babel/plugin-transform-arrow-functions等。但是我们不可能为每一个特性都添加一个插件,所以可以用"preset"预设一组插件。

preset-env: 一组预设,包含支持现在的JavaScript(ES2015, ES2016等)的所有插件。babel官方帮我们做了一些预设的插件集,称之为preset,这样我们只需要使用对应的preset就可以了。以JS标准为例,babel提供了如下的一些preset:
es2015/es2016/es2017/env
es20xx的preset只转译该年份批准的标准,而env则代指最新的标准,包括了latest和es20xx各年份
另外,还有 stage-0到stage-4的标准成形之前的各个阶段,这些都是实验版的preset,建议不要使用。

Polyfill:
Babel只转译新标准引入的语法为ES5语法,对于新标准引入的新原生对象,部分原生对象新增的原型方法,新增的API等,如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise、Object.assign() , Array.from,Array.prototype.includes等,不会转译,所以需要引入Polyfill。

  • 安装:
    npm install --save babel-polyfill (安装为dependency而不是devDependency)
  • 使用:
    1.Node: import “@babel/polyfill”;
    2.webpack配置: module.exports = { entry: ["babel-polyfill", "./app/js"] };
    (要确保在入口处导入polyfill,因为polyfill代码需要在所有其他代码前先被调用)

babel-polyfill包只是把core-js和regenerator-runtime包装了下,这两个包才是polyfill的核心。用于应用程序而不是library/tool.(用babel-node时会自动加载此polyfill)

Runtime:
babel-polyfill会把ES2015+环境整体引入到代码环境中,让代码可以直接使用新标准所引入的新原生对象,新API等,但是会污染全局环境。

对library/tool而言,用transform-runtime插件

Babel使用非常小的helper来执行常见功能,例如_extend。默认情况下,这将添加到需要它的每个文件中。这种重复有时是不必要的,尤其是当应用程序分布在多个文件上时。 这是@ babel / plugin-transform-runtime插件的用武之地:所有helper都将引用模块@ babel / runtime以避免编译输出中的重复,runtime将编译到构建的代码中。
此转换器的另一个目的是为代码创建沙盒环境。如果使用@ babel / polyfill及其提供的内置函数(如Promise,Set和Map),则会污染全局范围。虽然这可能适用于应用程序或命令行工具,但如果代码是打算发布供其他人使用的library/tool,或者如果无法准确控制代码运行的环境,则会出现问题。

transform-runtime 插件做了如下事情:

  • core-js aliasing:自动导入babel-runtime/core-js,并将全局静态方法、全局内置对象 映射到对应的模块。
  • Helper aliasing:将内联的工具函数移除,改成通过babel-runtime/helpers模块进行导入,比如_classCallCheck工具函数。
  • Regenerator aliasing:如果你使用了 async/generator 函数,则自动导入 babel-runtime/regenerator模块。

transform-runtime 和 babel-runtime:
将transform-runtime安装为开发依赖,runtime安装为生产依赖,runtime本身将取决于所部署的代码。

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

// .babelrc
{
    "plugins": ["@babel/plugin-transform-runtime"]
}

例:transform-runtime插件把js代码中使用到的新原生对象和静态方法转换成对runtime实现包的引用。

// 输入的ES6代码
var sym = Symbol();
// 通过transform-runtime转换后的ES5+runtime代码 
var _symbol = require("babel-runtime/core-js/symbol");
var sym = (0, _symbol.default)();

3.Babel配置

Options: https://babeljs.io/docs/en/options
配置文件:.babel.config.js or .babelrc?
https://babeljs.io/docs/en/configuration

babel.config.js:

const presets = [
  [
    "@babel/env",
    {
      targets: {
        edge: "17",
        firefox: "60",
        chrome: "67",
        safari: "11.1",
      },
    },
  ],
];

module.exports = { presets };

以上基于babel 7.4.x版本。

扩展阅读:
babel: https://babeljs.io/docs/en/
babel-plugin-transform-runtime/babel-runtime细节:
https://babeljs.io/docs/en/babel-plugin-transform-runtime#technical-details
babel插件手册:
https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md
Babel7应用:
https://blog.hhking.cn/2019/04/02/babel-v7-update/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值