Babel 入门指南
对
Babel学习总结,有理解错误的地方,希望大佬指正。
插件版本
以下为本文所提及的所有插件版本:
| 分类 | 模块 | 版本 |
|---|---|---|
| 工具包 | @babel/core | ^7.14.6 |
| @babel/cli | ^7.14.5 | |
| presets | @babel/preset-env | ^7.14.7 |
| @babel/preset-typescript | ^7.14.5 | |
| @babel/preset-react | ^7.14.5 | |
| plugins | @babel/plugin-transform-runtime | ^7.14.5 |
| @babel/plugin-transform-arrow-functions | ^7.14.5 | |
| polyfill | @babel/polyfill | ^7.12.1 |
| runtime | @babel/runtime | ^7.14.6 |
| @babel/runtime-corejs2 | ^7.14.6 | |
| @babel/runtime-corejs3 | ^7.14.7 | |
| webpack | webpack | ^5.43.0 |
| webpack-cli | ^4.7.2 | |
| babel-loader | ^8.2.2 |
基本概念
你需要的所有
Babel模块都将作为单独的npm包发布,其范围为@babel(自版本 7 开始)。这种模块化设计允许每种工具都针对特定用例设计。
@babel/core
Babel 的核心功能容纳于 @babel/core 模块。作为终端用户我们无需关注该库,该库将作为其他工具库的依赖项。
npm i -D @babel/core
@babel/cli
@babel/cli 是一个允许你在终端使用 babel 的工具:
npm i -D @babel/cli
npx babel src --out-dir lib
以上命令,将 src 中的 .js 文件通过 babel 编译后,输出至 lib 文件夹。由于我们还没有设置转换规则,这里输出代码将与输入保持一致。
我们可以指定我们想要的转换规则,通过把它们作为选项传给 CLI,接下来会在 plugins presets 中讲解。
我们使用上面的 --out-dir 选项。你可以通过使用 npx babel --help 运行它来查看 cli 工具接收的其余选项。但对我们来说最重要的是 --plugins --presets。
转换规则 syntax/api
babel 在转译的时候,会将源代码分成 syntax 和 api 两部分来处理:
syntax-对新特性语法糖进行转换,如:const, let -> var() => {} -> function(){}等;api-对PromiseMap等新的内置对象进行转换,如:babel/polyfill会在全局挂在一个window.Promise补丁方法。
一般情况,plugins presets 用于 syntax 转换,polyfill 用于 api 转换。
plugins
转换规则会体现为插件的形式,插件是小型 JavaScript 程序,它指示 Babel 如何进行代码转换。你甚至可以编写自己的插件,来应用你想要的任何转换规则。
想要将 ES2015+ 语法转换为 ES5,我们可以依赖类似 @babel/plugin-transform-arrow-functions 这样的官方插件,该插件会将 箭头函数 转换为 普通函数,如:
npm i -D @babel/plugin-transform-arrow-functions
npx babel src --out-dir lib --plugins=babel/plugin-transform-arrow-functions
运行以上代码:
In
// src/index.js
const fn = () => console.log('hello babel');
Out
// lib/index.js
const fn = function () {
return console.log('hello babel');
};
presets
前面我们介绍了如何将 箭头函数 转换为 普通函数,当我们的项目中使用了大量 ES2015+ 的新语法时,就需要配置大量的 plugins。
presets 包含着一组预先设定的插件,而不是逐一添加我们想要的所有插件。
就像使用 plugins 一样,你也可以创建自己的 preset,分享你需要的任何插件组合。在这个例子中,我们使用了 @babel/preset-env:
npm i -D @babel/preset-env
npx babel src --out-dir lib --presets=@babel/preset-env
这个 preset 包括支持现代 JavaScript(ES2015,ES2016 等)的所有插件。
polyfill
该插件因污染全局作用域,自
Babel v7.4.0起,@babel/polyfill已被弃用。推荐使用@babel/plugin-transform-runtime方案。
polyfill 即补丁的意思。@babel/polyfill 模块包括:
core-js-提供新语法API的集合库(缺PromiseAPI,那么就手写实现一个);regenerator-runtime-提供generator函数 API。
这意味着你可以使用像 Promise 或 WeakMap 这样的新内置函数,像 Array.from 或 Object.assign 这样的静态方法,像 Array.prototype.includes 这样的实例方法,以及 generator 函数。
为了做到这一点,@babel/polyfill 会在全局作用域和类似 String 这样的内置对象的原型对象上添加对象或方法(同时也会造成全局作用域污染)。
以下完整示例:
# 注意:使用 --save 选项,而不是 --save-dev,这是因为 polyfill 需要在运行时中在源代码之前执行。
npm i --save @babel/polyfill
// .babelrc.json
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage"
}
]
]
}
.babelrc.json 中 @babel/preset-env 的配置包含 useBuiltIns 属性,该属性有三个取值:
false-Boolean类型,默认为 false,会将整个polyfill引入;entry-String类型,会将core-jsregenerator-runtime中目标浏览器缺少的所有方法引入;usage-String类型,按需引入,当前项目使用过的新语法,但目标浏览器版本不支持的方法。
In
// src/index.js
import '@babel/polyfill';
const map = new Map([
['name', 'muzi'],
['age', 28],
]);
Out
"use strict";
require("core-js/modules/es6.map.js");
require("core-js/modules/es6.string.iterator.js");
require("core-js/modules/es6.object.to-string.js");
require("core-js/modules/es6.array.iterator.js");
require("core-js/modules/web.dom.iterable.js");
var map = new Map([['name', 'muzi'], ['age', 28]]);
core-js@3之后,@babel/polyfill就被弃用了,示例如下:
npm install --save core-js@3
// .babelrc.json
module.exports = {
presets: [
['@babel/preset-env', {
targets: {
ie: '8',
},
useBuiltIns: 'usage',
corejs: '3',
}],
],
}
In
// src/index.js
import 'core-js/stable';
import 'regenerator-runtime/runtime';
const map = new Map([
['name', 'muzi'],
['age', 28],
]);
Out
// lib/index.js
"use strict";
require("core-js/modules/es.array.iterator.js");
require("core-js/modules/es.map.js");
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.string.iterator.js");
require("core-js/modules/web.dom-collections.iterator.js");
require("core-js/stable");
require("regenerator-runtime/runtime");
var map = new Map([['name', 'muzi'], ['age', 28]]);
配置文件
根据需要,我们可以将
pluginspresets的配置从命令行中抽离出来,甚至可以对pluginspresets传参,以定制转换规则,以下列出了多种格式的babel配置文件。
babel.config.json & .babelrc.json
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
}
}
]
]
}
将该配置置于项目根目录,执行 npx babel 命令时,babel(v7.8.0+) 会自动识别该文件,该文件表示使用 @babel/preset-env 并将 targets 中浏览器没有的特性转换为 ES5 代码。
查阅 babel.config.json 文档 以查看更多配置选项。
babel.config.js & .babelrc.js
我们可以在 js 文件中定义 babel 配置:
module.exports = (api) => {
api.cache(true);
const presets = [
[
'@babel/preset-env',
{
targets: {
edge: '17',
firefox: '60',
chrome: '67',
safari: '11.1',
},
},
],
];
return {
presets,
}
}
使用 js 配置 babel,使我们可以访问任何 Nodejs API。例如基于 process 环境变量的动态配置:
const presets = [ ... ];
const plugins = [ ... ];
if (process.env["ENV"] === "prod") {
plugins.push(...);
}
module.exports = { presets, plugins };
package.json
可以在 package.babel 中编写配置,规则与 babel.config.json .babelrc.json 一致:
{
// ...
"babel": {
"presets": ["@babel/preset-env"]
}
// ...
}
环境差异化配置
babel 支持根据不同的环境配置,运用不同的转译规则,可以通过 BABEL_ENV 或 NODE_ENV 对环境进行赋值,如果没有环境变量,babel 默认取 development。示例如下:
cross-env BABEL_ENV=production npx babel src --out-dir lib
# or
cross-env NODE_ENV=production npx babel src --out-dir lib
{
"env": {
"development": {
"presets": [...]
},
"production": {
"presets": [...]
}
}
}
小结
@babel/cli从终端运行Babel;pluginspresets用于指定目标浏览器转换规则,syntax转换;polyfill即补丁,用于填充目标浏览器中缺少的功能,如ES6新的内置函数PromiseMap等,api转换;- 我们可以通过
BABEL_ENV或NODE_ENV为babel添加环境差异化配置。
presets
@babel/preset-env
npm i -D @babel/preset-env
@babel/preset-env 支持所有 ES2015+ 新语法的 syntax 转换,即对新特性语法糖的转换,如 let, const -> var () => {} -> function(){} 等。
@babel/preset-env 是整个 Babel 大家族最重要的一个 preset。
除了进行语法转换,该预设还可以通过设置参数项进行针对性语法转换以及 polyfill 的部分引入。
@babel/preset-env 的参数很多,这里重点讲解最为重要的四个参数 targets useBuiltIns corejs modules。
targets
该参数项用来确定目标浏览器环境,而目标浏览器版本是否具备某语法特性的数据来自 Can I use。
babel 会根据目标浏览器对语法的支持程度,来进行对应的语法转换。
targets 可以取值为字符串、字符串数组或对象,不设置的时候取默认值空对象 {}:
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
// browsers 与 browserslist 写法完全一致
// browsers: ['last 2 versions'],
chrome: '58',
ie: '11',
},
},
],
],
}
useBuiltIns
useBuiltIns 主要与 polyfill 行为相关,决定代码转换后,需要引入哪些补丁包:
false-Boolean类型,默认为 false,会将整个polyfill引入;entry-String类型,会将core-jsregenerator-runtime中目标浏览器缺少的所有方法引入;usage-String类型,按需引入,当前项目使用过的新语法,但目标浏览器版本不支持的方法。
PS:该属性需配合 @babel/polyfill 使用。
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
chrome: '58',
ie: '11',
},
useBuiltIns: 'usage',
},
],
],
}
corejs
前面我们讲解过,
core-js是新api的集合包。
corejs 属性只有当 useBuiltIns 值为 usage 或 entry 时生效。
corejs 属性用于指定 babel 转码使用的 core-js 版本,取值有两个:
2-Number类型,默认值为 2,babel转码使用core-js@2版本;3-Number类型,babel转码使用core-js@3版本。因为某些新API只有core-js@3里才有,根据实际需求,决定是否使用 3。
modules
modules 可选 "amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false,默认为 "auto"。
该项用来设置是否把 ES Module 语法改成其它模块化语法。
cjs 是 commonjs 的缩写,如果将 modules 设置为 false,babel 将不会对 ES Module 进行转换,由于 ES Module 是编译时静态编译,在使用 Webpack 一类的打包工具,可以进行静态分析,从而可以做 tree shaking 等优化措施。
@babel/preset-typescript
npm i -D @babel/preset-typescript
@babel/preset-typescript 和 @babel/preset-react 类似,是将特殊语法转换为 JS。
@babel/preset-typescript 的原理是直接将 typescript 的代码移除,所以我们不需要 typescript 库,这使得编译速度变得更快。
@babel/preset-typescript 不会对 ts 代码做类型检查,而是将类型检查交给了 IDE 去做(eslint tslint)。
npx babel index.ts --out-file index.js --presets=@babel/preset-typescript
In
// index.ts
interface SumI {
(a: number, b: number): Number;
}
const sum: SumI = (a, b) => a + b;
Out
// index.js
const sum = (a, b) => a + b;
未关注 @babel/preset-typescript 的其他参数,babel 通过 @babel/preset-typescript 转换为 js 后,再将其交给 @babel/preset-env 处理;
@babel/preset-env 会帮助我们完成 syntax 语法转换,以及 polyfill/runtime 的处理。
@babel/preset-react
npm i -D @babel/preset-react
@babel/preset-react 将 JSX 语法转换为 React.craeteElement() JS 语法。
npx babel index.jsx --out-file index.js --presets=@babel/preset-react
In
// index.jsx
import React, { useState } from 'react';
const Example = () => {
const [count, setCount] = useState(0);
return (
<div onClick={() => setCount}>
{count}
</div>
);
};
Out
// index.js
import React, { useState } from 'react';
const Example = () => {
const [count, setCount] = useState(0);
return /*#__PURE__*/React.createElement("div", {
onClick: () => setCount
}, count);
};
plugins
@babel/plugin-transform-runtime
@babel/plugin-transform-runtime 的功能:
处理 helpers-自动移除语法转换后内联的辅助函数(inline Babel helpers),使用@babel/runtime/helpers里的辅助函数来替代;处理 core-js API-当代码里使用了core-js的API,自动从@babel/runtime-corejs{2, 3}/core-js中引入,以此来替代全局引入的core-js/modules;处理 Generator/async-当代码里使用了Generator/async函数,自动引入@babel/runtime/regenerator,以此来替代全局引入的regenerator-runtime/runtime
配置项
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| helpers | Boolean | true | 是否要自动引入辅助函数包 |
| corejs | Boolean|String | false | 可选 false 2 3,是否对 core-js API 进行转换,避免全局污染。false-不转换,假如代码中使用了 Promise,babel 转换时不会对 Promise 做任何处理;2-使用 @babel/runtime-corejs2 进行转换。假如代码中使用了 Promise,babel 将提供 var _promise = require("@babel/runtime-corejs2/core-js/promise") 给转换后的代码使用;3-使用 @babel/runtime-corejs3 进行转换。 |
| regenerator | Boolean | true | 是否对 Generator/async 函数进行转换 |
| useESModules | Boolean | false | 是否使用 ES Modules,在用 webpack 一类的打包工具的时候,我们可以设置为 true,以便做静态分析。 |
| absoluteRuntime | Boolean | String | false | 该项用来自定义 @babel/plugin-transform-runtime 引入 @babel/runtime/ 模块的路径规则,取值是布尔值或字符串。没有特殊需求,我们不需要修改,保持默认 false 即可。 |
运行时模块
@babel/runtime @babel/runtime-corejs{2, 3} 中都包含了所有 syntax 转换时需要的 helper 函数,以及 regenerator;
@babel/runtime-corejs{2, 3} 则包含了 core-js 的内容(@babel/runtime-corejs{2, 3} = @babel/runtime + core-js{2, 3});
@babel/runtime @babel/runtime-corejs{2, 3} 与 @babel/polyfill 非常类似,都是运行时引入,所以安装需要使用 --save;
npm install --save @babel/runtime
# or
npm install --save @babel/runtime-corejs2
# or
npm install --save @babel/runtime-corejs3
@babel/runtime @babel/runtime-corejs{2, 3} 功能作用基本相同,根据需要,选择安装其中一个:
- 当
@babel/plugin-transform-runtimecorejs为false时,安装@babel/runtime。该条件下,babel不会对core-js API进行处理,所以不需要安装core-js; - 当
@babel/plugin-transform-runtimecorejs为2 | 3时,安装@babel/runtime-corejs{2, 3};
处理 helpers
babel 在转译的过程中,对 syntax 的处理可能会使用到 helper 函数。如下所示:
// .babelrc.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
chrome: '58',
ie: '11',
},
},
],
],
}
In
// lib.js
class A {
constructor() {
this.name = 'a';
}
}
export default A;
// index.js
import A from './lib';
class B extends A {
constructor() {
super();
this.name = 'b';
}
}
Out
// lib.js
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var A = function A() {
_classCallCheck(this, A);
this.name = 'a';
};
var _default = A;
exports.default = _default;
// index.js
"use strict";
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
var _lib = _interopRequireDefault(require("./lib"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
var B = /*#__PURE__*/function (_A) {
_inherits(B, _A);
var _super = _createSuper(B);
function B() {
var _this;
_classCallCheck(this, B);
_this = _super.call(this);
_this.name = 'b';
return _this;
}
return B;
}(_lib.default);
从上面的例子可以看到,我们在两个文件中使用了 ES6 class 语法,babel 在做 syntax 引入了一堆帮助函数 _classCallCheck _getPrototypeOf _createSuper 等等,_classCallCheck 函数在 lib.js index.js 都重复引入了,在实际项目中,这会让我们编译后的包变得非常臃肿。
@babel/plugin-transform-runtime 的作用,就是将所有 helper 函数的引入统一指向一个包,而这个包就是 @babel/runtime。
npm i @babel/runtime
npm i -D @babel/plugin-transform-runtime
// .babelrc.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
chrome: '58',
ie: '11',
},
},
],
],
plugins: [
'@babel/plugin-transform-runtime',
],
}
配置 @babel/plugin-transform-runtime 后,转换结果如下:
Out
// lib.js
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var A = function A() {
(0, _classCallCheck2.default)(this, A);
this.name = 'a';
};
var _default = A;
exports.default = _default;
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _lib = _interopRequireDefault(require("./lib"));
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
var B = /*#__PURE__*/function (_A) {
(0, _inherits2.default)(B, _A);
var _super = _createSuper(B);
function B() {
var _this;
(0, _classCallCheck2.default)(this, B);
_this = _super.call(this);
_this.name = 'b';
return _this;
}
return B;
}(_lib.default);
处理 core-js API & Generator/async
以下示例可以看到,转换后的代码未对 Promise 作任何处理,这是因为 @babel/plugin-transform-runtime 的 corejs 配置为默认的 false:
// .babelrc.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
chrome: '58',
ie: '8',
},
},
],
],
plugins: [
'@babel/plugin-transform-runtime',
],
}
In
// index.js
const fn = async () => {
await new Promise((resolve) => {
setTimeout(() => {
resolve('done')
}, 500);
});
}
fn();
Out
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var fn = /*#__PURE__*/function () {
var _ref = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee() {
return _regenerator["default"].wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return new Promise(function (resolve) {
setTimeout(function () {
resolve('done');
}, 500);
});
case 2:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return function fn() {
return _ref.apply(this, arguments);
};
}();
fn();
当我们设置 corejs: 2:
// .babelrc.js
plugins: [
['@babel/plugin-transform-runtime', {
corejs: '2',
}],
],
Out
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
var _regenerator = _interopRequireDefault(require("@babel/runtime-corejs2/regenerator"));
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/asyncToGenerator"));
var fn = /*#__PURE__*/function () {
var _ref = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee() {
var res;
return _regenerator["default"].wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return new _promise["default"](function (resolve) {
setTimeout(function () {
resolve('done');
}, 500);
});
case 2:
res = _context.sent;
console.log(res);
case 4:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return function fn() {
return _ref.apply(this, arguments);
};
}();
fn();
如上所示,@babel/polyfill 在处理 Promise 时,是直接挂载了 window.Promise 方法:
require("core-js/modules/es6.promise.js");
而 @babel/plugin-transform-runtime 则使用了 @babel/runtime-corejs2 中的 _promise 方法,该模块实现了与 Promise 完全相同的功能方法,供浏览器使用。这么做就避免了全局污染。
webpack 集成
我们可以使用 babel-loader 在 webpack 中集成 babel。以下示例简单介绍 @babel/preset-env 集成使用,如果需要转换 typescript react 等语法,与之同理。
安装依赖:
npm i -D @babel/core @babel/cli
npm i -D @babel/preset-env @babel/plugin-transform-runtime
npm i -S @babel/runtime-corejs2
npm i -D webpack webpack-cli
npm i -D babel-loader
业务代码:
// src/index.js
const fn = async () => {
const res = await new Promise((resolve) => {
setTimeout(() => {
resolve('done');
}, 1000);
});
console.log(res);
}
fn();
配置 babel:
// .babelrc.js
module.exports = {
presets: [
['@babel/preset-env', {
// babel 不会处理 ES Modules 语法
// webpack 对 import 语法做 Tree Shaking 优化,一定程度减少包体积
modules: false,
}],
],
plugins: [
['@babel/plugin-transform-runtime', {
corejs: '2',
}],
],
}
配置浏览器支持:
# .browserslistrc
> 1%
last 2 versions
not ie <= 8
配置 webpack:
// webpack.config.js
const path = require('path');
const resolvePath = (pathstr) => path.resolve(__dirname, pathstr);
module.exports = {
mode: 'development',
entry: {
main: resolvePath('./src/index.js'),
},
output: {
filename: 'index.js',
path: resolvePath('./lib'),
},
module: {
rules: [
{
test: /\.js$/,
include: [resolvePath('./src')],
use: 'babel-loader',
exclude: /node_modules/,
},
]
},
}
执行打包:
npx webpack --config webpack.config.js