一、前言:最开始接触babel是开始学习vue的时候,因为使用vue开发单页应用时使用了许多ES6/ES7的语法,当时只知道babel是用来将ES6/ES7语法转换为浏览器能识别的ES5,而且还错误地认为babel仅仅是webpack的一个插件,是为webpack而生的,后来随着慢慢学习,才发现babel是一个强大的工具链,它可以进行语法转换,polyfill(打补丁),源码转换,jsx语法转换等等一系列操作。在webpack上使用只是对它的一种应用。babel还可以在gulp,grunt,rollup等一系列打包工具上使用,除此之外babel官方提供了cli可以不借助打包工具使用。
二、简单使用(babel7.x)
- 安装包,@babel/core为核心包,@babel/cli为babel cli,用来执行babel,@babel/preset-env为插件,用来转换ES6/ES7或更高级的语法。如果只使用@babel/core,转换前与转换后相同,不做处理。@babel/polyfill,用来给新的API打补丁。
npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill
- 配置,在项目根目录下创建babel.config.js,targets表示支持的浏览器,useBuiltIns为babel7.x新增的一个属性
module.exports = {
presets: [
[
'@babel/env',
{
useBuiltIns: 'usage',
corejs: 2,
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1",
}
}
]
]
}
- 执行babel,npx为npm的包执行器
./node_modules/.bin/babel src --out-dir lib
//或者
npx babel src --out-dir lib
我们来对如下代码进行转换
let a = 23
let fn = () => 34
let obj = {
name: 'tom'
}
Object.assign(obj, { age: 23 })
当我们开启useBuiltIns时,
'use strict'
require('core-js/modules/es6.object.assign')
var a = 23
var fn = function fn() {
return 34
}
var obj = {
name: 'tom'
}
Object.assign(obj, {
age: 23
})
当不使用useBuiltIns时,可以看到Object.assign没有被转换
'use strict'
var a = 23
var fn = function fn() {
return 34
}
var obj = {
name: 'tom'
}
Object.assign(obj, {
age: 23
})
说明:@babel/polyfill 模块包括 core-js 和一个自定义的 regenerator runtime 模块用于模拟完整的 ES2015+ 环境,这意味着你可以使用诸如 Promise
和 WeakMap
之类的新的内置组件、 Array.from
或 Object.assign
之类的方法、 Array.prototype.includes 之类的实例方法以及生成器函数(generator functions)(前提是你使用了 regenerator 插件)。为了添加这些功能,polyfill 将添加到全局范围(global scope)和类似 String 这样的内置原型(native prototypes)中。对于软件库/工具的作者来说,这可能太多了。如果你不需要类似 Array.prototype.includes 的实例方法,可以使用 transform runtime 插件而不是对全局范围(global scope)造成污染的 @babel/polyfill。
幸运的是,我们所使用的 env
preset 提供了一个 "useBuiltIns"
参数,当此参数设置为 "usage"
时,就会加载上面所提到的最后一个优化措施,也就是只包含你所需要的 polyfill。
注意问题:babel7.x
- 当我们使用上述方法进行polyfill时,可以看到,新的API是通过全局注入的,这样可能造成全局污染,解决方法后面介绍
- 在babel7.x中,当我们使用useBuiltIns,执行babel代码转换时,控制台会给我们提示如下信息:
大概意思是:babel编译器告诉你,你在env中使用了useBuiltIns参数,但并没有显示声明core-js的版本,在当前版本没指定core-js时,会默认引入2.x版本的core-js;并且下面建议我们安装使用core-js2.x或者core-js3.x。在这里,当我们不指定的时候,默认使用2.x,如果指定为1的时候,babel编译器执行时就报错,也就是说我们要么不指定,要么就是2,要么就是3或者以后的更高版本。两者的的区别
//使用2.x时,导入的补丁如下
require("core-js/modules/es6.object.assign");
//使用3.x时,导入的补丁如下
require("core-js/modules/es.object.assign");
当我们使用2.x的时候,如果我们没有安装core-js2.x,npx babel编译的时候并不会报错,但时当我们打开core-js/modules时,里面并没有es6.object.assign文件,所以运行时会报错。所以当你开启useBuiltIns时,记得安装对应版本的core-js。
三、@babel/polyfill,@babel/runtime,@babel/plugin-transform-runtime三者的使用与区别。
- @babel/polyfill,上面已经介绍过了,用来polyfill想Object.assign,Array.form等新的API,使用方式,babel7.x之前可以在webpack中的入口处指定,也可以在main.js中直接引入,在babel7.x中,@babel/preset-env中新增加的参数useBuiltIns可以指定使用polyfill。
//相当于按需引入
presets: [
[
'@babel/env',
{
useBuiltIns: 'usage',
corejs:2
}
]
]
//相当于全部polyfill引入,不建议这么使用
//import '@babel/polyfill'
presets: [
[
'@babel/env',
{
useBuiltIns: 'entry',
corejs:2
}
]
]
- @babel/runtime:作用与@babel/polyfill一样,@babel/polyfill是相当于将对应的polyfill挂在构造函数的原型上,这样会造成原型污染,@babel/runtime相当于局部的polyfill,不会造成全局变量污染。
- @babel/plugin-transform-runtime:@babel/runtime的使用依赖@babel/plugin-transform-runtime(官网上说得)
针对上面提到的@babel/polyfill会产生原型污染和全局污染,我们使用@babel/runtime
plugins: [
[
'@babel/plugin-transform-runtime',
{
helpers: true,
corejs: 2
}
]
]
在这里我们没有使用@babel/polyfill,同样也能polyfill新的api
//源代码
let a = 23
let fn = () => 34
let obj = {
name: 'tom'
}
Object.assign(obj, { age: 23 })
使用@babel/runtime后
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
var _assign = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/assign"));
var a = 23;
var fn = function fn() {
return 34;
};
var obj = {
name: 'tom'
};
(0, _assign["default"])(obj, {
age: 23
});
注意:同样在这里我们打开node_modules文件夹下的@babel文件夹,发现并没有runtime-corejs2文件夹,这是因为babel7.x后@babel/runtime已经将其剔除了,里面只剩helpers和regenerator了,要使用,必须手动安装,安装方式如下
npm i @babel/runtime-corejs2 -S
使用时必须配置参数corejs:2
helpers:是babel转换时的一些辅助函数,在babel7.x之前我们可以在preset-env中的参数开启,在babel7.x之后已经从preset-env中被剔除,被加入到transform-runtime中,可以在其参数中设置其值为true开启,默认值为false
regenerator:可以将AST语法书转换成代码,可以在transform-runtime开启,默认值为false
helpers和regenerator在我们自定义插件时相当有用,一般情况可以不用管。
总结:在转换新的语法时我们使用preset-env,在polyfill新的API时,我们可以使用@babel/polyfill或者@babel/runtime,使用前者我们可以在preset-env中设置参数开启,也可以直接导入。使用后者时我们需要配合@babel/plugin-tranform-runtime插件。在babel7.x之前,我们直接安装@babbel/polyfill或者babel/rutime就行了,在7.x之后,当我们使用@babel/polyfill时需要手动安装core-js@2或者core-js@3作为生产依赖,当我们使用@babel/rutime是需要手动安装@babel/runtime-corejs2作为生产依赖。在@babel/preset-env中一些stage-n里的特性已经不被包含,比如在class使用箭头函数时,我们需要安装插件@babel/plugin-proposal-class-properties,然后在plugins里面配置,具体哪些不包含,我们可以在打包错我信息的提示中查看。
最后再说说babel在项目中使用的方法吧
- 创建babel.config.js(推荐做法)
- 创建.babelrc文件,以json格式配置
- 在npm的package.json中配置
- 在webpack中使用,还可以在babel-loader的参数中配置
//在babel.config.js中
module.exports = function (api) {
api.cache(true);
const presets = [ ... ];
const plugins = [ ... ];
return {
presets,
plugins
};
}
//在.babelrc中
{
"presets": [...],
"plugins": [...]
}
//在packag.json中
{
"name": "my-package",
"version": "1.0.0",
"babel": {
"presets": [ ... ],
"plugins": [ ... ],
}
}