babel是什么
babel官方文档中对babel有明确的定义:
Babel 是一个 JavaScript 编译器
Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
当然babel已不仅仅用于转换ECMAScript 2015+ ,例如它还可以转换typescript、flow。
babel做的工作是
将源代码解析为AST
更改/转换AST
打印AST(转换为源代码)
插件和preset
babel 本身不具有任何转化功能,它把转化的功能都分解到一个个 plugin 里面。因此当我们不配置任何插件时,经过 babel 的代码和输入是相同的。
插件
插件分两种:
- 语法插件
- 转换插件
【前端面试刷题网站:灵题库,收集大厂面试真题,相关知识点详细解析。】
前面已经提到,babel的插件实际做的工作是
将源代码解析为AST
更改/转换AST
打印AST(转换为源代码)
语法插件做的工作就是第1步,将源代码解析为AST,而转换插件则是3步都要进行。因此如果已经配置了转换插件,就不需要再额外配置相应的语法插件。这一点官方文档中也有说明:
转换插件将启用相应的语法插件,因此你不必同时指定这两种插件。
预设
因为通常我们在项目中使用es2015代码转换或者react语法转换,会用到多个插件,如果一个一个配置,繁琐而且容易出错。预设就是用来解决这个问题的。
Babel 的预设(preset)可以被看作是一组 Babel 插件和/或 options 配置的可共享模块。\
常用的preset:preset-env
,用来转换es2015,preset-react
,用来转换jsx等,preset-typescript
。
插件和预设的执行顺序
如果两个转换插件都将处理“程序(Program)”的某个代码片段,则将根据转换插件或 preset 的排列顺序依次执行。
• 插件在 Presets 前运行。
• 插件顺序从前往后排列。
• Preset 顺序是颠倒的(从后往前)。
babel的使用
babel主要有3种使用方法
- 命令行
- node API
- babel-loader
命令行
npm install --save-dev @babel/core @babel/cli @babel/preset-env
注:babel7中的核心逻辑被抽离到@babel/core中,因此使用babel必须安装babel/core
{
"presets": [
"@babel/preset-env"
]
}
使用babel命令行工具进行代码转换
./node_modules/.bin/babel src --out-dir lib
node API
npm install --save-dev @babel/core
const babel = require("@babel/core");
babel.transformSync("code", optionsObject);
集成(babel-loader)
{
// ...other config
module: {
rules: [
{
test: /.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
}
可以在.babelrc
、package.json
、babel-loader
的option
中配置插件和preset.
推荐.babelrc
。
使用babel进行polyfill
babel的一些插件和预设可以转换新语法特性(如箭头函数),而内建的变量(如Promise、Symbol)却无法兼容。
为了兼容内建变量,需要用到polyfill的技术。
polyfill技术就是用兼容语法(ES5)实现新的内建变量、扩展内建变量的静态属性(Array.from
)和实例属性([].includes
)。
polyfill库
最流行的polyfill库有两个:core-js
和regenerator
。
core-js
zloirock/core-js,它提供了 ES5、ES6 的 polyfills,包括 promise 、symbols、collections、iterators、typed arrays、ECMAScript 7+ proposals、setImmediate 等等。
regenerator
facebook/regenerator,它实现 ES6/ES7 中 generators、yield、async 及 await 等相关的语法。
babel-polyfill
babel-polyfill做的事情就是引入了core-js
和regenerator
,使用的时候直接import,如果使用webpack打包,直接在入口entry中引入即可。
在babel@7.4.0以后已经不推荐,可以直接引入core-js
和regenerator
代替。
@babel/runtime
同样是引入了core-js
和regenerator
,以支持polyfill的能力。
除了支持polyfill能力,@babel/runtime还提供了运行时代码复用的能力,后面会提到。
在之前的版本中都是直接引用所有的polyfill到项目中,这样有两个问题
- 体积过大,有些浏览器版本已经支持一些新的内建的对象和属性,不需要polyfill;项目中也不是每个polyfill的特性都用到,引用所有的polyfill会造成很大的冗余。
- 污染全局变量,polyfill方法是创建全局的变量以支持内建变量,给内建变量添加静态方法或者实例属性,这样会污染到全局变量。
preset-env
preset-env支持通过设置useBuildIn
选项来控制polyfill的体积。
该选项有以下几个可选值
false
这是默认的值,选择该值,preset-env不做操作,需要手动引入polyfil(babel-polyfill
或者core-js
+ regenerator
)
entry
需要手动引polyfill,preset-env会根据browserslist改写引用polyfill的语句(包括babel-polyfill
、core-js
、regenerator
),引入必要的polyfill,而不会安装环境中已经支持的特性,从而达到缩减包体积的目的。
例如我们项目中手动引入了core-js
import "core-js";
preset-env会转换为
// 根据browserslist判断,只引入环境中不支持的特性
import "core-js/modules/es.string.pad-start";
import "core-js/modules/es.string.pad-end";
usage
使用该值,不需要手动引入polyfill,preset-env会自动给用到某个特性的文件添加polyfill的引入,这样就实现了按需加载。
babel-plugin-transform-runtime
插件功能
这个插件有两个功能
让所有用到babel的runtime的文件,只引用一份运行时,就是@babel/runtime。
自动引入core-js和regenerater,并给core-js和@babel/polyfill的内置(如Promise、Map、Set)命名别名,这样可以避免污染全局变量。
下面两种配置都可以支持按需加载,也都能支持实例属性。
第一种配置会污染全局,第二种不会。
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
]
]
}
{
"presets": [
[
"@babel/preset-env"
]
],
"plugins": [
["@babel/plugin-transform-runtime", {
"corejs": 3
}]
]
}
core-js2只支持全局变量和静态属性,而core-js3已经可以支持实例属性。
babel corejs@3 是如何按需polyfill原型对象方法的
注意事项
- 在
core-js3
之前,如果需要实例属性的polyfill,只能用污染全局的方式。 - 使用
@babel/plugin-transform-runtime + corejs@3
,需要安装@babel/runtime-corejs3
:https://www.babeljs.cn/docs/babel-plugin-transform-runtime#corejs -
babel-plugin-transform-runtime
依赖@babel/runtime
,插件是dev安装,runtime
要生产环境安装:https://www.babeljs.cn/docs/babel-plugin-transform-runtime#installation。