Babel 介绍
中文官网 https://babel.docschina.org/docs/en/
ES6 支持情况 http://kangax.github.io/compat-table/es6/
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 的注入帮助代码以节省代码大小