Babel6 与Babel7 配置具有一定的差异,在Babel的发展历程中,我们有必要对其配置有一定的了解,才能在项目中正常合理的配置Babel的编译,配置错误会导致出现冗余代码、项目构建产物过大或浏览器不兼容等问题。
依赖安装
pnpm i @babel/core @babel/cli @babel/preset-env -D
默认转换规则
默认只转语法,不转API
新的API
- 新增的全局对象:
Promise
、Map
、Symbol
、Proxy
、Iterator
等 - 新增的实例方法:
[].find()
,''.includes
,Object.assign()
等
Babel的配置文件
默认会在当前项目根目录查找 .babelrc
、.babelrc.js
、.babelrc.json
、babel.config.json
、babel.config.js
、package.json
babel.config.js
module.exports = {
"presets": [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3.6.5"
}
]
]
}
Babel7.8及以下版本支持直接json配置
babel.config.json
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3.6.5"
}
]
]
}
package.json
配置方式
{
"name": "my-package",
"version": "1.0.0",
"babel": {
"presets": ["@babel/preset-env"],
"plugins": [ ... ],
}
}
预设和插件
babel的配置主要分为 presets
预设 和 plugins
插件
每一个ES版本都会新增一些新特性,这些新特性,babel
会提供相应的插件 plugins-list,每一个插件对应一个npm包
比如 ES2017新增了 async
和 await
,babel提供了 @babel/plugin-transform-async-to-generator
用于将 async/await
转换为 generator
函数,
async function foo() {
await bar();
}
// ==>
var _asyncToGenerator = function (fn) {
...
};
var foo = _asyncToGenerator(function* () {
yield bar();
});
ES2018 新增了对象...展开,babel 提供 @babel/plugin-proposal-object-rest-spread
来转换
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }
// ===>
let n = { x, y, ...z };
console.log(n); // { x: 1, y: 2, a: 3, b: 4 }
由于插件太多,Babel提供的了预设功能,用于简化配置,预设是一系列插件的集合,常用预设有:
@babel/preset-env
@babel/preset-typescript
@babel/preset-react
@babel/preset-flow
预设和插件的的短名称
@babel/preset-env
简写@babel/env
或env
, 可省略preset-
@babel/plugin-proposal-object-rest-spread
简写@babel/proposal-object-rest-spread
,可省略plugin-
// babel.config.js
module.exports = {
"presets": ['@babel/env'],
"plugins": ["@babel/transform-decorators-legacy"]
}
预设和插件的执行顺序
- 插件比预设先执行
- 插件执行顺序是插件数组从前向后执行
- 预设执行顺序是预设数组从后向前执行
预设和插件版本
Babel7以前的版本,使用年代preset,如 babel-preset-es2015
、babel-preset-es2016
、babel-preset-es2017
、babel-preset-latest
、babel-preset-stage-0
、babel-preset-stage-1
、babel-preset-stage-2
等。
Babel7开始推荐使用 @babel/preset-react
、@babel/preset-env
、@babel/preset-flow
、@babel/preset-typescript
常用插件主要是 @babel/plugin-transform-runtime
babel-polyfill
polyfill
是大浏览器中添加缺失的新特性,补齐API和标准对齐,比如,浏览器不支持Promise,那么polyfill
会添加 window.Promise
来支持 promise
polyfill
是为当前环境提供一个垫片。所谓垫片,是指垫平不同浏览器之间差异的东西。polyfill提供了全局的ES6对象以及通过修改原型链 Array.prototype
等实现对实例的实现。
Babel polyfill 使用的几种方式
- 直接在html中引用构建好的
polyfill.js
- 在工程入口文件中引用
polyfill.js
- 在工程入口文件中引用
@babel/polyfill
- 在前端工程的入口文件里引入
core-js/stable
与regenerator-runtime/runtime
; - 在前端工程构建工具的配置文件入口项引入
polyfill.js
; - 在前端工程构建工具的配置文件入口项引入
@babel/polyfill
; - 在前端工程构建工具的配置文件入口项引入
core-js/stable
与regenerator-runtime/runtime
;
// import './polyfill.js'
// import '@babel/polyfill'
import "core-js/stable";
import "regenerator-runtime/runtime";
const fn1 = (num) => num + 1;
const path = require('path');
module.exports = {
// entry: ['./polyfill.js', './a.js'],
// entry: ['@babel/polyfill', './a.js'],
entry: ['core-js/stable', 'regenerator-runtime/runtime', './a.js'],
output: {
filename: 'b.js',
path: path.resolve(__dirname, '')
},
mode: 'development'
};
以上引用方式是Babel7以前的使用方式,这种方式都是全量引用所有的新特性垫片,这样引用的 polyfill 包会大增加我们构建的js体积大小,如果我们只用到了其中两三个新特性,那就存在很多冗余的垫片。
@babel/preset-env
@babel/preset-env
是 Babel6
时代 babel-preset-latest
的增强版。该预设除了包含所有稳定的转码插件,还可以根据我们设定的目标环境进行针对性转码。
配置
module.exports = {
// 三种配置方式等同
presets: ["@babel/env"],
// presets: [["@babel/env", {}]],
// presets: [["@babel/env"]],
plugins: []
}
@babel/env
是 @babel/preset-env
的简写,核心配置项有 targets
、useBuiltIns
、modules
、corejs
targets
用于设置兼容到哪些目标浏览器,如果不配置,则尝试读取 package.json
和 .browserslistrc
中的配置, browserslist
的配置也同样作用于 autoprefixer
、postcss
等插件。 如果没有 targets
和 browserslist
配置,则转换所有的ES6语法为ES5版本
useBuiltIns
取值有 usage/entry/false
,默认为 false
false
不使用polyfill
,只转换语法entry
会根据目标浏览器环境,引用未支持的所有的polyfill
,需要在入口文件引用@babel/polyfill
usage
会先分析代码中使用到的新特性,只为用到的新特性添加polyfill
,不需要手动添加@babel/polyfill
,但需要配置corejs
的版本,不配置会有警告。
WARNING (@babel/preset-env): We noticed you're using the `useBuiltIns` option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the `corejs` option.
You should also be sure that the version you pass to the `corejs` option matches the version specified in your `package.json`'s `dependencies` section. If it doesn't, you need to run one of the following commands:
npm install --save core-js@2 npm install --save core-js@3
yarn add core-js@2 yarn add core-js@3
"browserslist": [
"chrome >= 49"
],
配置 usage
,只为使用到的新特性提供 polyfill
module.exports = {
presets: [['@babel/env', {
useBuiltIns: 'usage'
}]]
}
源码
const configPromise = Promise.resolve({url: '/login'})
babel转换后
"use strict";
require("core-js/modules/es6.object.to-string.js");
require("core-js/modules/es6.promise.js");
const configPromise = Promise.resolve({
url: '/login'
});
corejs
取值为 2 或 3, 3 是 2 的升级版,会扩展一些新的API的polyfill,比如数组的flat方法,一般使用 3 的版本。
安装相应的模块
npm install --save core-js@2
npm install --save core-js@3
modules
模块语法转换
用于将当前ES6的模块语法转换为其它模块化的语法
取值 amd/umd/systemjs/commonjs/cjs/auto/false
,默认为 auto
,会转换为 commonjs
语法
常见的模块化语法有两种:
- ES6的模块法语法用的是
import
与export
; - commonjs模块化语法是
require
与module.exports
。
一般建议设置为 false
,不转换 es6 的模块语法,方便构建工具如 webpack
、rollup
对模块进行静态分析,实现 tree shaking
等优化措施
@babel/plugin-transform-runtime
usage
的缺点: 语法转换后,代码是会注入一些辅助函数,如果有很多个js文件,每个文件顶部都会注入相关辅助函数,有可能会注入导致辅助函数重复,最后用构建工具打包出来的产物会非常大。
class Person {
sayname() {
return 'name'
}
}
转换后
"use strict";
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
var Person = /*#__PURE__*/function () {
function Person() {
_classCallCheck(this, Person);
}
_createClass(Person, [{
key: "sayname",
value: function sayname() {
return 'name';
}
}]);
return Person;
}();
为了实现 class
语法,添加了 _classCallCheck
、_createClass
、_defineProperties
几个辅助函数。
@babel/runtime
为了在很多js转换情况下,避免辅助函数直接注入到代码中,babel提供了 @babel/runtime
来提供这些辅助函数单独引用。
@babel/runtime
只是提供了辅助函数模块,但还不能在转换过程中自动替换,这需要 @babel/plugin-transform-runtime
插件来提供自动替换辅助函数的功能。
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var Person = /*#__PURE__*/function () {
function Person() {
(0, _classCallCheck2.default)(this, Person);
}
(0, _createClass2.default)(Person, [{
key: "sayname",
value: function sayname() {
return 'name';
}
}]);
return Person;
}();
函数注入手动改成函数引用,减少了函数直接注入造成的构建产物增大的问题
实际生产中,我们需要同时安装这两个模块,注意由于辅助函数最终是需要打包到构建产物中的,@babel/runtime
需要安装到 dependencies
下
npm install --save @babel/runtime
npm install --save-dev @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-runtime
plugin-transform-runtime 的作用
- 自动移除转换后的内联辅助函数,使用
@babel/runtime/helpers
里的辅助函数来替代 - 当代码里使用了
core-js
的API,自动引入@babel/runtime-corejs3/core-js-stable/
,以此来替代全局引入的core-js/stable
; - 当代码里使用了
Generator/async
函数,自动引入@babel/runtime/regenerator
,以此来替代全局引入的regenerator-runtime/runtime
;
2和3的核心功能是使用函数来替换原生api,以达到不污染全局对象的目的( polyfill
和core-js/stable
与regenerator-runtime/runtime
都是直接对原生对象做扩展,会污染原生对象)。
polyfill
的方式是直接扩展原生对象,比如浏览器不支持Promise
,那么 polyfill 的作用就是在window上实现Promise对象,生成一个window.Promise
,这样会污染原生环境。plugin-transform-runtime
的方式是添加_promise
辅助函数来替换原来的 api
// babel.config.js
module.exports = {
presets: [['@babel/env']],
plugins: [
['@babel/transform-runtime', {
corejs: 3
}]
]
}
指定 corejs
版本,才能对相关的API做替换,指定版本后,需要安装相应的corejs版本,有两种方式
- 安装
@babel/runtime
和core-js@3
- 安装
@babel/runtime-corejs3
// 原始代码
var obj = Promise.resolve();
// polyfill
require("core-js/modules/es.promise.js");
var obj = Promise.resolve();
// plugin-transform-runtime
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));
var obj = _promise["default"].resolve();
默认配置
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": false,
"helpers": true,
"regenerator": true,
"version": "7.0.0-beta.0"
}
]
]
}
absoluteRuntime
默认为false
, 也就是默认从当前项目根目录下的node_modules
下引用@babel/runtime
,当前没有的话,需要手动指定路径。helpers
默认为true
,即自动引入辅助函数替换内联函数。corejs
默认为false
, 不对不对Promise这一类的API进行转换,仍然使用core-js
提供的polyfill
。regenerator
默认为true
,自动对regenerator/async
api 进行转换。version
这里对应的是@babel/runtime
或@babel/runtime-corejs2/3
的版本。如果安装了@babel/runtime 的后续版本(或者它们的 corejs 对应版本,比如@babel/runtime-corejs3) ,或者将其列为依赖项,则转换运行时可以使用更高级的特性。
依赖于 @babel/runtime-coregs2@7.7.4
,则可以使用
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 2,
"version": "^7.7.4"
}
]
]
}
一般前端业务开发,使用默认值就好,即不用添加任何配置
// regenerator: false (直接修改全局对象,添加polyfill)
require("regenerator-runtime/runtime.js");
// regenerator: true (返回 _regenerator 工具函数)
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
使用场景
业务开发
- 插件
@babel/plugin-transform-runtime
使用默认配置即可 @babel/preset-env
的useBuiltIns
设置为usage
,corejs
设置为3
module.exports = {
"presets": [
[
"@babel/preset-env",
{
// 不转换模块类型,仍然使用 ES Module,方便构建工具 tree shaking
"modules": false,
// 使用 usage,只为代码用到的新特性提供 polyfill
"useBuiltIns": "usage",
// 使用 corejs3版本提供的 polyfill
"corejs": 3
}
],
],
// 引入辅助函数替换内联函数
"plugins": ["@babel/plugin-transform-runtime"]
}
npm模块开发
- 插件
@babel/plugin-transform-runtime
的corejs
配置为2或3 - 发布为ES模块时,
@babel/preset-env
的modules
要设置为false
,不转换模块类型,仍然使用 ES Module,方便构建工具 tree shaking
阅读参考