实践-Babel

264 阅读1分钟

你是否也对项目里引入的这样那样的 babel 库感到头晕?如 babel-core@babel/preset-env,那就一起来实践看看吧。

@babel-core

首先是 babel-core,其中包含了将代码字符串转换为 ast 等核心功能。

// babel.js
const fs = require('fs');
const babel = require('@babel/core');

const source = fs.readFileSync('src/before.js', 'utf8'); // 读取代码文件

const result = babel.transformSync(source, { // 对代码字符串进行转换
  ast: true,
});

// src/before.js
const sayHello = (name) => `Hello, ${name}`;

转换后的 AST 内容如下:

{
  "type": "File",
  "start": 0,
  "end": 45,
  "loc": {
    "start": { "line": 1, "column": 0 },
    "end": { "line": 2, "column": 0 }
  },
  "errors": [],
  "program": {
    "type": "Program",
    "start": 0,
    "end": 45,
    "loc": {
      "start": { "line": 1, "column": 0 },
      "end": { "line": 2, "column": 0 }
    },
    "sourceType": "module",
    "interpreter": null,
    "body": [
      {
        "type": "VariableDeclaration",
        "start": 0,
        "end": 44,
        "loc": {
          "start": { "line": 1, "column": 0 },
          "end": { "line": 1, "column": 44 }
        },
        "declarations": [
          {
            "type": "VariableDeclarator",
            "start": 6,
            "end": 43,
            "loc": {
              "start": { "line": 1, "column": 6 },
              "end": { "line": 1, "column": 43 }
            },
            "id": {
              "type": "Identifier",
              "start": 6,
              "end": 14,
              "loc": {
                "start": { "line": 1, "column": 6 },
                "end": { "line": 1, "column": 14 },
                "identifierName": "sayHello"
              },
              "name": "sayHello"
            },
            "init": {
              "type": "ArrowFunctionExpression",
              "start": 17,
              "end": 43,
              "loc": {
                "start": { "line": 1, "column": 17 },
                "end": { "line": 1, "column": 43 }
              },
              "id": null,
              "generator": false,
              "async": false,
              "params": [
                {
                  "type": "Identifier",
                  "start": 18,
                  "end": 22,
                  "loc": {
                    "start": { "line": 1, "column": 18 },
                    "end": { "line": 1, "column": 22 },
                    "identifierName": "name"
                  },
                  "name": "name"
                }
              ],
              "body": {
                "type": "TemplateLiteral",
                "start": 27,
                "end": 43,
                "loc": {
                  "start": { "line": 1, "column": 27 },
                  "end": { "line": 1, "column": 43 }
                },
                "expressions": [
                  {
                    "type": "Identifier",
                    "start": 37,
                    "end": 41,
                    "loc": {
                      "start": { "line": 1, "column": 37 },
                      "end": { "line": 1, "column": 41 },
                      "identifierName": "name"
                    },
                    "name": "name"
                  }
                ],
                "quasis": [
                  {
                    "type": "TemplateElement",
                    "start": 28,
                    "end": 35,
                    "loc": {
                      "start": { "line": 1, "column": 28 },
                      "end": { "line": 1, "column": 35 }
                    },
                    "value": { "raw": "Hello, ", "cooked": "Hello, " },
                    "tail": false
                  },
                  {
                    "type": "TemplateElement",
                    "start": 42,
                    "end": 42,
                    "loc": {
                      "start": { "line": 1, "column": 42 },
                      "end": { "line": 1, "column": 42 }
                    },
                    "value": { "raw": "", "cooked": "" },
                    "tail": true
                  }
                ]
              }
            }
          }
        ],
        "kind": "const"
      }
    ],
    "directives": []
  },
  "comments": []
}

@babel/plugin-transform-arrow-functions

代码的转换均由 plugin 来完成,如 plugin-transform-arrow-functions 的功能是将箭头函数转换为普通的函数,这给了 babel 很大的扩展性。

const result = babel.transformSync(source, {
  plugins: ['@babel/plugin-transform-arrow-functions'], // 引入 plugin
  ast: true,
});

转换前后的代码:

// 转换前
const sayHello = (name) => `Hello, ${name}`;

// 转换后
const sayHello = function (name) {\n  return `Hello, ${name}`;\n};

nice~ 👍

我们甚至可以编写自己的 babel plugin,来实现我们想要的代码转换。如编写一个 plugin,来将 const 转换为 let

// plugin.js
module.exports = function () {
  return {
    visitor: {
      VariableDeclaration(path) {
        const node = path.node;
        node.kind = node.kind === 'const' ? 'let' : node.kind;
      },
    },
  };
};

// babel.js
const result = babel.transformSync(source, {
  plugins: ['./plugin']
});

// 输出结果
"code": "let sayHello = name => `Hello, ${name}`;"

@babel/cli

提供命令行操作。

安装后在 package.json 中添加命令:

 "scripts": {
    "babel": "babel src/index.js  --out-file output/index.js" //指定输入和输出
  },

添加一个 babel.config.json 来定义 babel 配置:

{
  "plugins": ["@babel/plugin-transform-arrow-functions"]
}

运行 npm run babelyarn babel 即可看到输出结果:

// output/index.js
const sayHello = function (name) {
  return `Hello, ${name}`;
};

@babel/preset-env

一个 babel plugin 一般只专注做一件事,为了完成一个复杂功能,有时我们需要引入一组 plugin。babel 配置中有一个 presets 的选项,可以将它当作一组插件的集合。

比如 “@babel/preset-env”,它的功能非常强大。它默认支持所有现代 JavaScript 语法的转换,如 ES2015, ES2016 等。

使用时只需在 babel.config.json 添加:

{
  "presets": [["@babel/env"]]
}

运行 yarn babel 的输出结果:

"use strict";

var sayHello = function sayHello(name) {
  return "Hello, ".concat(name);
};

它可以通过传入需要支持的浏览器 来做对应的语法转换,如:

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

polyfill

用于模拟一个完整的 ES2015+ 环境,在运行环境缺少新的特性(如 Promise)时,在环境中添加对应的 polyfill。

推荐和 @babel/preset-env 一起使用,它将在遇到目标浏览器不支持的特性时添加对应的 polyfill。这样我们就可以放心使用 ES 的新语法了,nice~👍

{
  "presets": [
    [
      "@babel/env",
      {
        "targets": {
          "edge": "17",
          "firefox": "60",
          "chrome": "40", // 设置为较低版本测试 polyfill
          "safari": "11.1"
        },
        "useBuiltIns": "usage" // 添加此声明即可
      }
    ]
  ]
}

添加一个 Promise 用于测试:

// index.js
const sayHello = (name) => `Hello, ${name}`;

Promise.resolve().then(() => {
  console.log('Hello World~');
});

// output.js
"use strict";

require("core-js/modules/es6.object.to-string.js");

require("core-js/modules/es6.promise.js"); // 引入 polyfill 文件

var sayHello = function sayHello(name) {
  return "Hello, ".concat(name);
};

Promise.resolve().then(function () {
  console.log('Hello World~');
});

参考

babel 官方文档