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
-对Promise
Map
等新的内置对象进行转换,如: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
的集合库(缺Promise
API,那么就手写实现一个);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-js
regenerator-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]]);
复制代码
配置文件
根据需要,我们可以将
plugins
presets
的配置从命令行中抽离出来,甚至可以对plugins
presets
传参,以定制转换规则,以下列出了多种格式的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
;plugins
presets
用于指定目标浏览器转换规则,syntax
转换;polyfill
即补丁,用于填充目标浏览器中缺少的功能,如ES6
新的内置函数Promise
Map
等,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-js
regenerator-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 进行转换,避免全局污染。
|
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-runtime
corejs
为false
时,安装@babel/runtime
。该条件下,babel
不会对core-js API
进行处理,所以不需要安装core-js
; - 当
@babel/plugin-transform-runtime
corejs
为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
复制代码