学习Babel

全篇共 4048 字。按500字/分钟阅读,预计用时 8.1 分钟。总访问 157 次,日访问 2 次。

Babel 是 JavaScript 编译器。关于 Babel 神话故事,传说中人类在远早语言相通,不顾上帝的阻挠齐心协力修建名为巴别的通天塔,被激怒的上帝施法让人类说不同的语言,沟通的困难导致通天的巴别塔最终没有建成。Babel 常与Grunt、Gulp、Rollup 和 Webpack 等构建工具一同使用。我在 GitHub 创建了一个和本篇文章对应的项目:learn-babel

环境准备

Babel 运行在 Node.js 环境中。检查系统中是否安装了 node 和 npm。从 Node.js 官网 下载 node。
node -v
npm -v

创建项目

创建目录 learn-babel,作为学习 Babel 使用:
mkdir learn-babel

进入项目:
cd learn-babel

初始化 NPM 配置,自动创建 package.json 和 package-lock.json:
npm init

安装Babel

打开终端。进入 learn-babel 项目。使用 NPM 安装 Babel 的核心模块。开发者就可以导入该模块并使用 babel 提供的各种个编译有关的方法:
npm i -D @babel/core

Babel 还有命令行工具,用于从命令行编译 JavaScript 文件。安装 Babel 的命令行工具:
npm i -D @babel/cli

npm 会自动将依赖包安装到 node_modules 目录中,并将依赖项加入 package.json 中的 devDependencies 属性内:

"devDependencies": {
  "@babel/cli": "^7.8.4",
  "@babel/core": "^7.8.4"
}

@babel/cli用法

首先测试一下 Babel 的命令行工具 @babel/cli 是否正确安装到项目的 node_modules 目录内,并且在 .bin 目录中包含 babel 可执行文件。在项目根目录下,终端执行:
./node_modules/.bin/babel

输出类似下方 Babel 命令行工具的错误提示,表示 Babel 命令行工具已经可用。

babel:
  stdin compilation requires either -f/--filename [filename] or --no-babelrc

@babel/core用法

然后测试下 Babel 的核心依赖 @babel/core 是否正确安装。在项目根目录下创建 index.js 文件,文件中的内容如下:

  const babel = require('@babel/core');
  console.log(babel);

终端打印出 Babel 对象包含的所有属性,表示 Babel 核心依赖已经可用:

{
  Plugin: [Function: Plugin],
  File: [Getter],
  buildExternalHelpers: [Getter],
  resolvePlugin: [Getter],
  resolvePreset: [Getter],
  version: [Getter],
  ...
}

不妨用 Babel 核心编译器尝试转换代码,将 index.js 文件内容改成如下:

const babel = require('@babel/core');

const compiler = babel.transformSync(`
const name = '帅华君';
const foo = name => {
  console.log(name);
}
`);

console.log(compiler.code);

终端打印如下:

const name = '帅华君';

const foo = name => {
  console.log(name);
};

打印正常。因为我们还没有告诉 Babel 编译器如何转换 JavaScript 代码,所以 ES6 新增的箭头函数、常量都原样输出了。

Babel插件

Babel 中有预设和插件的概念,每一个插件负责一种编译转换,比如就有专门负责转换 ES6 的箭头函数语法的插件,将这一插件作为开发依赖安装:

npm i -D @babel/plugin-transform-arrow-functions

此时 package.json 中的开发依赖是这样的:

"devDependencies": {
  "@babel/cli": "^7.8.3",
  "@babel/core": "^7.8.3",
  "@babel/plugin-transform-arrow-functions": "^7.8.3"
}

继续修改 index.js 成下面的内容,在转换时告诉 Babel 使用 @babel/plugin-transform-arrow-functions 插件,专门将代码中的箭头函数转换成 ES5 中的样子,并且保留 ES6 箭头函数的特性,比如箭头函数对 this 指向在词法作用域上的规范:

const babel = require('@babel/core');

const compiler = babel.transformSync(`
const name = '帅华君';
const foo = name => {
  console.log(name);
  console.log(this);
}
`, {
  plugins: [
    '@babel/plugin-transform-arrow-functions'
  ]
});

console.log(compiler.code);

不出所料,打印出的转换后的代码如下,仅转换了箭头函数。箭头函数中的 this 指向它在词法作用域中的引用,如果在浏览器中运行就是 window 对象:

var _this = this;

const name = '帅华君';

const foo = function (name) {
  console.log(name);
  console.log(_this);
};

Babel预设

预设,预先设置插件,Babel 插件不同的组合方式,称作预设。Babel 中常用的预设是 @babel/preset-env,使用该预设要指定需要支持浏览器厂商和支持到何种程度。

安装 @babel/preset-env 预设:
npm i -D @babel/preset-env

Babel全部工具函数

  • babel.transform(code, options, callback) => { code, map, ast } 方法将在未来废弃。

  • babel.transformSync(code, options) => { code, map, ast }。

    const sourceCode = `
    const name = '帅华君';
    const foo = name => {
    console.log(name);
    console.log(this);
    }
    `;
    const result = babel.transformSync(sourceCode, {
    	plugins: [['@babel/plugin-transform-arrow-functions']],
    });
    console.log(result.code);
    
  • babel.transformAsync(code, options) => Promise<{ code, map, ast }>

    const sourceCode = `
    const name = '帅华君';
    const foo = name => {
    console.log(name);
    console.log(this);
    }
    `;
    (async function () {
      const result = await babel.transformAsync(sourceCode, {
        plugins: [
          ['@babel/plugin-transform-arrow-functions']
        ]
      });
      console.log(result.code);
    })();
    
  • babel.transformFile(filename, options, callback) => undefined

    const result = babel.transformFile('src/index.js', {
      plugins: [
        ['@babel/plugin-transform-arrow-functions']
      ]
    }, (err, result) => {
      if(err){
        console.log(err);
        return;
      }
      console.log(result.code);
    });
    
  • babel.transformFileSync(filename, options) => { code, map, ast }

    const result = babel.transformFileSync('src/index.js', {
      plugins: [
        ['@babel/plugin-transform-arrow-functions']
      ]
    });
    console.log(result.code);
    
  • babel.transformFileAsync(filename, options) => Promise<{ code, map, ast }>

    (async function(){
      const result = await babel.transformFileAsync('src/index.js', {
        plugins: [
          ['@babel/plugin-transform-arrow-functions']
        ]
      });
      console.log(result.code);
    })()