babel的安装和使用
官网:babeljs.io/
民间中文网:www.babeljs.cn/
不同的浏览器或不同版本的浏览器对JS标准的支持程度往往是不一样的,这就给开发者带来了很大的困扰
babel的目标就是把使用不同标准书写的JS代码,编译为统一的、能被各种浏览器识别的JS代码
babel的工作流程和postcss、webpack十分类似,babel本身仅提供了源码分析的功能,剩下的代码转换工作是交付给具体的插件来完成的
babel的安装
babel可以和构建工具联合起来使用,也可以单独使用
如果要单独使用babel,就需要安装下面两个库:
-
@babel/core
这是babel的核心库,它提供了编译所需要的所有api
-
@babel/cli
该库提供了一些cli命令,以便开发者调用核心库中的api来完成编译
npm i -D @babel/core @babel/cli
babel的使用
编译单个文件:
babel 要编译的文件所在位置 -o 编译结果文件防止的位置 [-w]
编译整个目录中的所有文件:
babel 要编译的目录所在位置 -d 编译结果放置的目录的位置 [-w]
-w是可选参数,当源文件内容发生变化时,babel会自动重新对源文件进行编译
babel的配置
babel本身没有做什么事情,真正的编译要依托于babel插件和babel预设来完成
预设是多个插件的集合体,用于解决一系列常见的兼容问题
需要通过在工程根目录中创建一个配置文件.babelrc来应用babel插件和babel预设
// .babelrc
{
"presets": [], // 用于应用babel预设
"plugins": [] // 用于应用babel插件
}
babel预设
最常用的babel预设是@babel/preset-env,它可以让开发者编写新标准的JS语法
经过该预设编译过后,编译结果文件的开头会加入"use strict",即结果代码是在严格模式下运行的
安装:
npm i -D @babel/preset-env
配置:
// .babelrc
{
"presets": [
"@babel/preset-env"
]
}
若有多个预设,则预设的运行先后顺序是presets数组的倒序顺序,位于数组后面的预设会先被运行,和webpack的loader类似
设置兼容范围
@babel/preset-env需要根据兼容的浏览器范围来确定如何编译
和postcss一样,可以使用文件.browserslistrc来定义浏览器的兼容范围
last 3 version
> 1%
not ie <= 8
预设自身的配置
如果需要给预设本身加入配置,则需要写为下面的形式(插件也类似):
// .babelrc
{
"presets": [
["@babel/preset-env", {
"配置1": "配置内容",
"配置2": "配置内容",
...
}]
]
}
useBuiltIns
常见的预设配置是useBuiltIns
该配置的默认值为false,表示预设仅转换新语法,但不对新的API进行处理
API就是JS运行环境所提供的一些函数或对象,例如Promise构造函数就是一个API
有些API是伴随着新标准一起出现的,因此不支持新标准的浏览器也不会有这些API
babel对API进行的处理,就是将这些API手写出来,让浏览器能够使用,具体的手写工作并不是由babel来完成的,但babel知道哪些库已经手写好了这些API,因此转换时babel会将库导入进来,这样JS就可以使用到这些手写的API了
将该配置设置为"usage"后,预设就可以从core-js库中导入相应的模块来得到功能相同的手写的API
例如"core-js/modules/es6.promise"模块中导出了一个手写的Promise构造函数
在useBuiltIns为true(转换后为true)的情况下,编译结果中就会导入该模块,因此之后使用的Promise构造函数就是该模块所提供的
在useBuiltIns为false的情况下,编译结果中不会导入该模块,文件中所使用的Promise就是js的运行环境所提供的Promise
预设本身并没有包含core-js,因此将useBuiltIns设置为true后,还需要开发者手动安装core-js
npm i core-js
不同版本的core-js中模块的存放位置以及模块名称会有些许变化,而该预设默认使用的是2版本的core-js,因此使用的也是2版本的core-js中模块的路径,如果安装的core-js版本不是2,则需要提供core-js的版本给预设
// .babelrc
{
"presets": [
["@babel/preset-env", {
"useBuiltIns": "usage",
"corejs": 3, // 设置babel所使用的core-js库的版本
...
}]
]
}
该预设并不能将所有的语法都进行转换,例如async和await,该预设就处理不了
但预设也可以通过导入另一个库regenerator-runtime中的某个模块来实现类似于async和await的功能
因此使用该预设时,还需要安装regenerator-runtime
npm i regenerator-runtime
babel插件
除了预设可以转换代码外,插件也可以转换代码,插件和预设之间的运行顺序如下:
- 插件在预设之前运行
- 插件运行的顺序是注册的顺序
- 预设运行的顺序是注册顺序的逆序
通常情况下,@babel/preset-env只转换那些已经成为正式标准的语法,对于还没有成为正式标准,仍处于早期阶段的语法,该预设是不做处理的
对于这些处于早期阶段的语法,要想转换就需要使用插件
插件的注册方式如下:
// .babelrc
{
"plugins": [
"插件名称1",
["插件名称2", 插件配置对象]
]
}
常用的插件有以下几个:
-
@babel/plugin-proposal-class-properties
该插件会将类中的字段初始化代码进行转换
class A { a = 1; // 字段初始化代码 constructor(){ } } -
@babel/plugin-proposal-optional-chaining
过去对对象内部的一个深层次属性进行验证时,为了避免代码运行时报错,就需要使用下面的方式:
var obj = { foo: { bar: { baz: 42 } } } // 判断obj.foo.bar.baz是否等于42 if(obj && obj.foo && obj.foo.bar && obj.foo.bar.baz === 42){ ...; }这种写法过于麻烦,因此ES官方提议了下面的语法(目前还未成为正式标准)
var obj = { foo: { bar: { baz: 42, }, }, }; if(obj?.foo?.bar?.baz){ ...; }当?.前面的内容为undefined时,该表达式就会立刻结束,就可以避免做大量的判断
该插件就能够对这种语法进行转换
-
babel-plugin-transform-remove-console
该插件用于将源代码中所有的
console.xxx(...)语句去除 -
@babel/plugin-transform-runtime
若多个源代码文件中使用了同一个API时,则这些源代码所编译形成的编译结果中就会出现重复的API定义代码
该插件就用于提取编译结果中出现的重复的自定义API,让它们全部变为导入runtime库的语句,避免了代码冗余
由于编译结果中可能会导入runtime库,因此安装该插件时还需要安装runtime库
npm i @babel/runtime
在webpack中使用babel
在webpack中使用babel-loader和@babel/core来应用babel
安装babel-loader和@babel/core:
npm i -D @babel/core babel-loader
在wepback配置中加入babel:
// webpack.config.js
module.exports = {
module: {
rules: [{
test: /\.js$/,
use: ["babel/loader"]
}]
}
}
注意:不要忘记给babel安装代码转换的插件和预设