Babel学习笔记

778 阅读7分钟

Babel 介绍

中文官网 https://babel.docschina.org/docs/en/

英文官网 https://babeljs.io/

ES6 支持情况 http://kangax.github.io/compat-table/es6/

专项语法支持 https://caniuse.com/

babel.config.json 配置项

{
  "preset": [],   // 预设
  "plugins": []   // 插件
}

1、通用依赖

// 核心库、babel 客户端、babel 预设
npm install --save-dev @babel/core @babel/cli @babel/preset-env
// babel 兼容性处理
npm install --save @babel/polyfill

2、ES3-ES5 插件

@babel/plugin-transform-member-expression-literals

转换成员表达式(把保留字赋值方式改成 [] 声明方式)

添加 babel.config.json 配置

{
  "plugins": [    // 引用的插件内容
    "@babel/plugin-transform-member-expression-literals"  // ES3 
  ]
}
obj.const = 'isKeyWord'
// 转变
obj["const"] = 'isKeyWord'

@babel/plugin-transform-property-literals

转换属性(把生成对象的保留字专程 [] 声明方式)

var foo = {
  const: function() {},
  var: function() {},

  default: 1,
  [a]: 2,
  foo: 1,
};
// 转变
var foo = {
  "const": function _const() {},
  "var": function _var() {},
  "default": 1,
  [a]: 2,
  foo: 1
};

@babel/plugin-transform-reserved-words

重命名系统保留字

var abstract = 1;
var x = abstract + 1;
// 转换
var _abstract = 1;
var x = _abstract + 1;

@babel/plugin-transform-property-mutators

已经包含在 @babel/preset-env

属性转换(把对象的 get\set 方法转换 Object.defineProperties 声明方式)

var foo = {
  get bar() {
    return this._bar;
  },
  set bar(value) {
    this._bar = value;
  },
};
// 转换
var foo = Object.defineProperties({}, {
  bar: {
    get: function get() {
      return this._bar;
    },
    set: function set(value) {
      this._bar = value;
    },
    configurable: true,
    enumerable: true
  }
});

3、ES2015 的插件

已经包含在 @babel/preset-env

依赖包: @babel/plugin-transform-arrow-functions 转换箭头函数,专成匿名函数

依赖包: @babel/plugin-transform-block-scoped-functions 函数块级作用域

依赖包: @babel/plugin-transform-block-scoping 块范围,处理 let\const 的块作用域也外层作用域变量冲突问题

{
  let a = 3;
}
let a = 3;

// 转换
{
  var _a = 3;
}
var a = 3;

依赖包: @babel/plugin-transform-classes 类的向后兼容(通过原型链处理)

依赖包: @babel/plugin-transform-computed-properties 处理计算属性兼容(定义函数处理:重复属性覆盖、使用 [] 处理计算属性赋值)

依赖包: @babel/plugin-transform-destructuring 处理解构语法

依赖包: @babel/plugin-transform-duplicate-keys 处理重复的对象属性(依赖计算属性插件 @babel/plugin-transform-computed-properties

依赖包: @babel/plugin-transform-for-of 兼容 for-of 循环(使用基本 for 循环改造)

依赖包: @babel/plugin-transform-function-name 为匿名函数表达式添加函数名(包括箭头函数,函数名为变量名)

let number = x => x;
let num = function(x) { return x }

// 转换
let number = function number(x) {
  return x;
};
let num = function num(x) {
  return x;
};

依赖包: @babel/plugin-transform-instanceof 处理 instanceof(多了一层 Symbol.hasInstance 判断)

依赖包: @babel/plugin-transform-literals 字面值处理(处理二进制、八进制、编码转换)

var b = 0b11; // binary integer literal
var o = 0o7; // octal integer literal
const u = "Hello\u{000A}\u{0009}!"; // unicode string literals, newline and tab

// 转换
var b = 3; // binary integer literal
var o = 7; // octal integer literal
const u = "Hello\n\t!"; // unicode string literals, newline and tab

依赖包: @babel/plugin-transform-new-target 兼容 new.target 使用

依赖包: @babel/plugin-transform-object-super 对象中 super 关键字使用

依赖包: @babel/plugin-transform-parameters 处理传参(包括参数结构、参数默认值、参数 rest)

依赖包: @babel/plugin-transform-shorthand-properties 各种简写兼容

var o = {a, b, c}
var cat = {
  getName() {  return name; }
}
// 转换
var a = {a: a, b: b, c: c}
var cat = {
  getName: function() { return name; }
}

依赖包: @babel/plugin-transform-spread 扩展操作符处理,需要和解构区分,符号相同使用场景不一样,进行的编码也不一样

var a = ["a", "b", "c"];
var b = [...a, "foo"];
var c = foo(...a);
// 转换
var a = ["a", "b", "c"];
var b = a.concat(["foo"]);
var c = foo.apply(void 0, a);

依赖包: @babel/plugin-transform-sticky-regex 正则表达式兼容(使用 RegExp 对象处理)

依赖包: @babel/plugin-transform-template-literals 字符串模版的使用(使用 concat 函数做字符串拼接)

依赖包: @babel/plugin-transform-typeof-symbol 兼容 typeof 对 Symbol 类型的判断

typeof Symbol() === "symbol";
// 转换
var _typeof = function(obj) {
  return obj && obj.constructor === Symbol ? "symbol" : typeof obj;
};
_typeof(Symbol()) === "symbol";

依赖包: @babel/plugin-transform-unicode-escapes 这是 Unicode 多位的支持,扩充了之前只能是4个16进制字符的限制

依赖包: @babel/plugin-transform-unicode-regex 对 Unicode 编码扩充在正则表达式用使用的兼容

4、ES2015+ 的插件

已经包含在 @babel/preset-env

依赖包: @babel/plugin-transform-exponentiation-operator 幂运算(用 Math.pow 解析 ** 运算符)

依赖包: @babel/plugin-transform-async-to-generator 异步转成生成器

依赖包: @babel/plugin-proposal-async-generator-functions 异步生成器转函数

依赖包: @babel/plugin-transform-dotall-regex 正则表达式兼容

依赖包: @babel/plugin-transform-named-capturing-groups-regex 命名正则表达式处理

var re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
console.log(re.exec("1999-02-29").groups.year);
// 转换
var re = _wrapRegExp(/(\d{4})-(\d{2})-(\d{2})/, { year: 1, month: 2, day: 3 });
console.log(re.exec("1999-02-29").groups.year);

依赖包: @babel/plugin-proposal-object-rest-spread 剩余属性解析

依赖包: @babel/plugin-proposal-unicode-property-regex Unicode编码的正则表达式

5、模块化插件

AMD 模块标准

依赖包: @babel/plugin-transform-modules-amd Async Module Definition 异步模块化定义

核心是通过 define 方法对无序的代码进行模块化定义,通过 require 方法实现代码的模块化加载

export default 42;

// 转换

define(["exports"], function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  var _default = 42;
  _exports.default = _default;
});

// 通过 require 方法异步加载

Commonjs 模块标准

依赖包: @babel/plugin-transform-modules-commonjs Node中使用的模块规范

export default 42;

// 转换 

Object.defineProperty(exports, "__esModule", {
  value: true,
});

exports.default = 42;

systemjs

依赖包: @babel/plugin-transform-modules-systemjs Systemjs 介绍 https://zhuanlan.zhihu.com/p/402155045

UMD 模块标准

依赖包: @babel/plugin-transform-modules-umd 同时兼容 AMD CMD Commonjs 的规范

amd 只能在浏览器中使用,commonjs只能在服务端(node) 环境中使用,umd 解决了这个问题。

export default 42;

// 转换

(function(global, factory) {
  if (typeof define === "function" && define.amd) {
    define(["exports"], factory);
  } else if (typeof exports !== "undefined") {
    factory(exports);
  } else {
    var mod = {
      exports: {},
    };
    factory(mod.exports);
    global.actual = mod.exports;
  }
})(this, function(exports) {
  "use strict";

  Object.defineProperty(exports, "__esModule", {
    value: true,
  });

  exports.default = 42;
});

6、预设 preset 配置

preset-env

依赖包: @babel/preset-env babel.config.json 配置:

{
  "presets": [    // 预设内容
    [
      "@babel/preset-env",    // 配置兼容环境
      {
        "targets": {    // 目标环境
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1"
        },
        "useBuiltIns": "usage",    // 启用 polyfill
        "corejs": "3.6.5"    // polyfill 依赖 corejs 内容
      }
    ]
  ]
}

preset-typescript

依赖包: @babel/preset-typescript 这个插件包含了@babel/plugin-transform-typescript

  • 使用 --preset @babel/preset-typescript 指定预设插件
  • 需要给 @babel/cli 使用 --extensions .ts 指定解析 ts 文件
> babel --extensions .ts --presets @babel/preset-typescript src -d lib

例子🌰:

const x: number = 0;
// 转换
const x = 0;

在 babel.config.js添加 ts 配置

{
  "presets": ["@babel/preset-typescript"]
}

7、集成包

cli

依赖包: @babel/cli 用作命令行编译文件

  • 入口点脚本在 @babel/cli/bin 中;
  • 有一个 shell 可执行脚本 babel-external-help.js 文件;
  • 主要的 babel cli 脚本 babel.js 文件

可选编译命令:

  • 编译文件 script.js 输出到 stdout,如: npx babel script.js --out-file 或 -o 输出到指定文件,如: npx babel script.js -o script-compiled.js
  • -- watch 或 -w 每次更改文件时编译文件,如: npx babel script.js -w -o script-compiled.js
  • --source-maps 或 -s 添加映射文件,如: npx babel script.js -o script-complied.js -s
  • --source-maps inline 使用内联映射,如: npx babel script.js -o script-compiled.js -s inline
  • --out-dir 或 -d 编译整个目录(src)到另一个目录(lib),如: npx babel src -d lib;编译整个目录(src)输出为单个文件,如: npx babel src -o script-compiled.js
  • --ignore 忽略文件(如规范和测试文件),如:npx babel src -d lib --ignore "src/**/*.test.js"
  • --copy-files 赋值不会编译的文件,如: npx babel src -d lib --copy-files
  • < 通过标准输入管道输入文件将其输出到 script-compiled.js,如: npx babel -o script-compiled.js < script.js
  • --plugins 指定在编译中使用的插件,多插件用逗号"," 间隔,如: npx babel script.js -o script-compiled.js --plugins=@babel/proposal-class-properties,@babel/transform-modules-amd
  • --presets 指定编译中使用的预设,多预设用逗号","间隔,如:npx babel script.js -o script-compiled.js --presets=@babel/preset-env,@babel/flow
  • --no-babelrc 忽略项目 .babel.json 或者 .babelrc 文件中的配置,并使用 cli 选项,如:npx babel --no-babelrc script.js -o script-compiled.js --preset=@babel/preset-env,@babel/preset-react
  • --config-file 自定义配置文件路径 npx babel --config-file /path/to/babel.config.json -d lib ./src

polyfill

依赖包: @babel/polyfill 依赖包:core-js 用作兼容高阶语法

  • polyfill 需要在源代码之前运行,也就是dependency依赖类型,而不是 devDependency
  • 包含一个自定义的regenerator 运行时core-js
  • 需要配置 useBuiltIns 选项,以便 polyfill 不包含需要的内容
  • 配置 "useBuiltIns": "usage" 选项会自动加载 @babel/plugin-transform-runtime

一个坑🕳

在安装 @babel/cli 时候引入了 core-js 包,然后在 preset-env 中添加了 useBuiltIns 和 corejs 的配置,但是在编译高阶语法的时候得到的结过,无法在根据编译之后的引入路径找到对应的文件,webpack 打包也抛出路径找不到异常:

"use strict";
require("core-js/modules/es.regexp.exec.js");
require("core-js/modules/es.string.replace.js");
require("core-js/modules/esnext.string.replace-all.");
console.log('hello, world!'.replaceAll(',', '')); 

babel 和 webpack 官网中都没有明确说明原因,特别是在 webpack 打包方式不需要 @babel/cli 情况下,给的错误就很突然。 调查 core-js 是一个单独的库,需要独立安装,所以要想 polyfill 真正有用需要执行 npm i core-js

runtime

依赖包: @babel/plugin-transform-runtime 可以重用 Babel 的注入帮助代码以节省代码大小