本文介绍了babel的配置和用法,对presets、plugins等概念及其用法进行了介绍;阐述了polyfill 和 transform-runtime 的作用和用法,对两者进行了对比;通过实验验证各自的特点和优势。 希望能对你有所帮助,欢迎交流~
一、什么是babel
将es6+进行转化,向后兼容 转化为es5使得他的绝大部分浏览器可以使用
下面地址可查看es6的支持情况:
kangax.github.io/compat-tabl…
二、用法
安装babel相关的依赖
npm install --save-dev @babel/core @babel/cli @babel/preset-env //@babel/cli实现命令行的能力
npm install --save @babel/polyfill
配置
以前配置使用.babelrc文件,现在可以使用bable.config.js 放在项目根路径下;进行babel转义时,如果没有指定对应的配置,就会走这套配置。
命令行编译src下文件:
./node_modules/.bin/babel src --out-dir dist
或
npx babel src --out-dir dist
babel也是可以进行文件配置的,而不是仅仅只可以像这里一样使用命令行,接下来看一下文件配置。 在跟目录下新建babel.config.js文件
const presets = [
[
"@babel/env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
"ie": "10"
},
}
]
]
module.exports={
presets
}
如果不指定特定的转义插件,就会走到配置文件
"scripts": {
"build": "babel src -d dist --presets=@babel/env",
"build:auto": "babel src -d dist",
"test": "echo \"Error: no test specified\" && exit 1"
},
三、Plugins & Presets
Presets是Plugins的集合 Plugins用来为不同特定特性转化为es6,不同的特性转化需要引入不同的Plugins,这样引入就很繁杂和不好维护,特此才有了集合Presets 如箭头函数的转化:
npm install --save-dev @babel/plugin-transform-arrow-functions
./node_modules/.bin/babel src --out-dir lib --plugins=@babel/plugin-transform-arrow-functions
一般复杂的命令行可以通过脚本的方式方便维护:
"scripts": {
"build": "babel src -d dist",
"build:env": "babel src -d dist --presets=@babel/env",
"build:arrow": "babel src -d dist --plugins=@babel/plugin-transform-arrow-functions",
"test": "echo \"Error: no test specified\" && exit 1"
},
一般多数es6语法转化使用的是 preset-env
npm install --save-dev @babel/preset-env
./node_modules/.bin/babel src --out-dir lib --presets=@babel/env
四、polyfill
可以用polyfill来实现所有新的js功能,env只实现我们使用的功能的转换,实现目标浏览器中缺少的功能
安装
npm install --save @babel/polyfill
使用
{
"presets": [
[
"@babel/env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
},
"useBuiltIns": "usage",
}
]
]
}
转义效果
转义前
const sayHi = () => {
console.log(77777777);
}
Promise.resolve().finally();
sayHi()
转义后
"use strict";
require("core-js/modules/es6.promise");
require("core-js/modules/es6.object.to-string");
require("core-js/modules/es7.promise.finally");
var sayHi = function sayHi() {
console.log(77777777);
};
Promise.resolve().finally();
sayHi();
五、plugin详解
插件有很多可以安装指定插件后,在命令行、脚本、bablerc、babael.config.js 进行配置,在这以@babel/plugin-transform-member-expression-literals举例。
插件用法举例
@babel/plugin-transform-member-expression-literals 将和关键字同名的属性,修改为.['xxx']的形式 输入:
obj.foo = "isValid";
obj.const = "isKeyword";
obj["var"] = "isKeyword";
输出:
obj.foo = "isValid";
obj["const"] = "isKeyword";
obj["var"] = "isKeyword";
使用:
//命令行
npx babel --plugins @babel/plugin-transform-member-expression-literals src -d dist
//通过 Node API
const code = `obj.foo = "isValid";
obj.const = "isKeyword";
obj.var = "isKeyword";`
const result = require("@babel/core").transform(code, {
plugins: ["@babel/plugin-transform-member-expression-literals"]
});
console.log(result);
效果:

其他新es版本的新特性的语法特性转化在此不再赘述。
模块化转化插件
babel提供多种形式的模块化转化支持:
·modules-amd
·modules-commonjs
·modules-systemjs
·modules-umd
测试的时候同一份代码可以通过不同的插件转化,效果不一样
转化配置
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build:amd": "babel src/test.js --out-file dist/test-amd.js --plugins @babel/plugin-transform-modules-amd",
"build:common": "babel src/test.js --out-file dist/test-common.js --plugins @babel/plugin-transform-modules-commonjs",
"build:system": "babel src/test.js --out-file dist/test-system.js --plugins @babel/plugin-transform-modules-systemjs",
"build:umd": "babel src/test.js --out-file dist/test-umd.js --plugins @babel/plugin-transform-modules-umd",
"build": "npm run build:amd && npm run build:common && npm run build:system && npm run build:umd"
},
注:命令行可以合并在一起 如果命令行不指定插件就会去找.babelrc等配置 如果没有找到 就会不进行任何处理
原代码 test.js
export default 42;
转化后的
amd
define(["exports"], function (_exports) {
"use strict";
Object.defineProperty(_exports, "__esModule", {
value: true
});
_exports.default = void 0;
var _default = 42;
_exports.default = _default;
});
common
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _default = 42;
exports.default = _default;
system
System.register([], function (_export, _context) {
"use strict";
return {
setters: [],
execute: function () {
_export("default", 42);
}
};
});
umd
(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.test = mod.exports;
}
})(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports) {
"use strict";
Object.defineProperty(_exports, "__esModule", {
value: true
});
_exports.default = void 0;
var _default = 42;
_exports.default = _default;
});
在实验插件
www.babeljs.cn/docs/plugin…
一些有提案但还没有纳入正式标准的用法也有对应的插件的开发,可以使用对应插件就可以体验最新的提案。提案一般都是优势胜过劣势的,可以大胆的使用,一定可以提升研发的效能。
代码瘦身插件 Minification
www.babeljs.cn/docs/plugin…
使用他们可以去除无用代码或用更简洁的方式替换原来的代码,减少代码的体积。
去除console debugger的可以使用
六、polyfill vs runtime
polyfill
垫片,内容多,体积大 运行原代码前 先将垫片垫进去,
runtime
运行时 环境隔离,体积小,不做侵入
preset-env
plugins 的集成 可以按需引入使用useBuiltIns
七、总结与验证
转化的对象有两种:语法和API;
语法转化:
关于语法的转化是固定的,而且是开发依赖就已经转化好的,不涉及对新代码的引入,所以一般没有问题。
在配置文件中引入对应的语法插件或preset就可以
如:
const presets = [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
"ie": "10"
},
},
]
]
module.exports = {
presets
}
API支持
支持api都是需要在代码中先引入新api的定义代码的,
有两种方式:
1.使用polyfill
2.使用@babel/plugin-transform-runtime
1.使用polyfill
使用polyfill的劣势:
1)自身总体很大: 可以考虑按需加载 2)会污染内置函数 静态方法 实例方法等,如果在项目的末端(开发项目本身或命令行终端)一般是没有影响的,可是如果在组件中要提供给别人用就会干扰其他人了:解决方案是使用@babel/plugin-transform-runtime
使用polyfill的方法
1.可以直接在代码开头引入polyfill,这样很大一般不推荐
2.使用@babel/env这个preset 里面有项配置useBuiltIns可以指定按需加载。
babel.config.js
const presets = [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
"ie": "10"
},
"useBuiltIns": "usage" //usage代表按需加载 此外还可以配置false:不按需加载 entry:从入口加载
},
]
]
module.exports = {
presets
}
转化前的源码
const sayHi = () => {
console.log(77777777);
}
Promise.resolve().finally();
sayHi()
转化后的代码
"use strict";
require("core-js/modules/es6.promise");
require("core-js/modules/es6.object.to-string");
require("core-js/modules/es7.promise.finally");
// import '@babel/polyfill'
var sayHi = function sayHi() {
console.log(77777777);
};
Promise.resolve().finally();
sayHi();
3.自己手动按需引入,这样一般效率会低很多 需要对各个core里面的内容都非常清楚,其实要求非常高。
2. plugin-transform-runtime 和 runtime
@babel/plugin-transform-runtime && @babel/runtime两个一般搭配起来一起用
来源背景
Babel 会使用很小的辅助函数来实现类似 _createClass 等公共方法。默认情况下,它将被添加(inject)到需要它的每个文件中。这样就会有一个问题:如果一个项目的多个不同的文件用到了同一个api,此时如果使用polyfill的方式,每个文件都会单独在头部引入,这样重复的代码就太多了,为了解决这个问题可以使用@babel/plugin-transform-runtime
这套方案的核心优势是:
1.同一个新api在这个项目中不同地方支持它时,引入的支持代码都从库@babel/runtime中引入,这样就避免重复了。
2.可以避免全局污染
plugin-transform-runtime避免重复的效果演示
转换前的代码
class Point { constructor(x, y) { this.x = x; this.y = y; }; getX() { return this.x; } } let cp = new ColorPoint(25, 8);
如果用folyfill按需转化 配置如下:
const presets = [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
"ie": "10"
},
"useBuiltIns": "usage"
},
]
]
module.exports = {
presets,
}
转化后的效果为:
"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); return Constructor; }
var Point = /*#__PURE__*/function () {
function Point(x, y) {
_classCallCheck(this, Point);
this.x = x;
this.y = y;
}
_createClass(Point, [{
key: "getX",
value: function getX() {
return this.x;
}
}]);
return Point;
}();
var cp = new ColorPoint(25, 8);
可以看到有很多inject插入 如果使用plugin-transform-runtime插件
const presets = [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
"ie": "10"
},
"useBuiltIns": "usage"
},
]
]
const plugins = [["@babel/plugin-transform-runtime"]]
module.exports = {
presets, plugins
}
转换后的代码
"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 Point = /*#__PURE__*/function () {
function Point(x, y) {
(0, _classCallCheck2.default)(this, Point);
this.x = x;
this.y = y;
}
(0, _createClass2.default)(Point, [{
key: "getX",
value: function getX() {
return this.x;
}
}]);
return Point;
}();
var cp = new ColorPoint(25, 8);
可以看到没有直接插入 而是通过@babel/runtime的方式引入的
避免全局污染效果演示
源码如下:
let isHas = [1, 2, 3].includes(2);
new Promise((resolve, reject) => {
resolve(100);
});
如果没有使用plugin-transform-runtime时,使用polyfill按需加载,配置文件如下
const presets = [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
"ie": "10"
},
"useBuiltIns": "usage"
},
]
]
module.exports = {
presets,
}
转化后的效果
"use strict";
require("core-js/modules/es6.promise");
require("core-js/modules/es6.object.to-string");
require("core-js/modules/es7.array.includes");
require("core-js/modules/es6.string.includes");
var isHas = [1, 2, 3].includes(2);
new Promise(function (resolve, reject) {
resolve(100);
});
可以发现Array.prototype 上新增了 includes 方法,并且新增了全局的 Promise 方法,污染了全局环境。
如果使用plugin-transform-runtime,配置如下:
const presets = [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
"ie": "10"
},
// "useBuiltIns": "usage"
},
]
]
const plugins = [["@babel/plugin-transform-runtime", { "corejs": 3 }]]
module.exports = {
presets, plugins
}
plugin-transform-runtime配置选项中要配指定要引入的corejs,不再使用"useBuiltIns": "usage" 因为他会和plugin-transform-runtime引入重复,这是转化后的效果:
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));
var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));
var _context;
var isHas = (0, _includes.default)(_context = [1, 2, 3]).call(_context, 2);
new _promise.default(function (resolve, reject) {
resolve(100);
});
没有直接去修改 Array.prototype,或者是新增 Promise 方法,避免了全局污染。 plugin-transform-runtime的优势可以总结为: 减少体积; 避免全局污染; 按需加载