Babel基础知识
# 01.Babel 基础知识
# Babel 是什么?
# Babel 是一个 JavaScript 编译器
Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。下面列出的是 Babel 能为你做的事情:
- 语法转换
- 通过 Polyfill 方式在目标环境中添加缺失的特性 (通过引入第三方 polyfill 模块,例如 core-js)
- 源码转换(codemods)
# Babel 的编译流程
Babel 是 source to source 的转换,整体编译流程分为三步:
- parse:通过 parser 把源码转成抽象语法树(AST)
- transform:遍历 AST,调用各种 transform 插件对 AST 进行增删改
- generate:把转换后的 AST 打印成目标代码,并生成 sourcemap
# 使用指南
1.安装以下依赖:
npm install --save-dev @babel/core @babel/cli @babel/preset-env
@babel/core:Babel 核心依赖包,提供了 transform、parse 等方法将代码转换成 ast 树并根据标准对其转换最后解析成符合要求的代码。
@babel/cli:Babel 自带了一个内置的 CLI 命令行工具,可通过命令行编译文件。安装依赖后 node_modules/.bin/下会多了 babel 命令,可用./node_modules/.bin/babel '需要转换的文件' --out-dir '输出的目录文件夹',来对文件进行解析。
@babel/preset-env:Babel 提供的预设可以根据配置的目标浏览器或者运行环境来自动将新版本的 js 的代码转换为 es5。
2.创建 babel.config.js 或者 babel.config.json
babel.config.json
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3.6.5"
}
]
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
edge: '17',
firefox: '60',
chrome: '67',
safari: '11.1'
},
useBuiltIns: 'usage',
corejs: '3.6.4'
}
]
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
3.运行此命令将 src 下的 index.js 编译到 dist 目录下
./node_modules/.bin/babel src/index.js --out-dir dist
或
npx babel src/index.js --out-dir dist
# 预设(Presets)
# 官方提供的预设
- @babel/preset-typescript:转换 TypeScript 代码
- @babel/preset-react:转换 React 代码
- @babel/preset-flow:转换 Flow 代码
- @babel/preset-env:用于转换 es6+的代码,代替了 babel-preset-es20xx
@babel/preset-env 常用参数:
targets:指定需要兼容的浏览器类型和版本
useBuiltIns:可选值 "usage" | "entry" | false 默认为 false,可以设置 useBuiltIns:usage 这样可以让编译后的文件按需倒入 corejs 的 polyfill
corejs:指定 corejs 版本
使用示例:
babel.config.json
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3.6.5"
}
]
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
编译前
const map = new Map()
编译后:
'use strict'
require('core-js/modules/web.dom-collections.iterator.js')
const map = new Map()
2
3
# 如何使用预设
babel.config.json 添加:
{
"presets": ["babel-preset-myPreset", "@babel/preset-env"]
}
2
3
# 预设的排列顺序
Preset 是逆序排列的(从后往前)。
{
"presets": ["a", "b", "c"]
}
2
3
# 预设的参数
插件和 preset 都可以接受参数,参数由插件名和参数对象组成一个数组,可以在配置文件中设置。
如果不指定参数,下面这几种形式都是一样的:
{
"presets": [
"presetA", // 使用字符串
["presetA"], // 使用数组
["presetA", {}] // 使用元组(tuple)第一个参数为预设名称,第二为预设参数的对象
]
}
2
3
4
5
6
7
# 插件(Plugin)
# 常见的插件
- @babel/plugin-proposal-class-properties:支持类内直接写属性和静态属性 如:
class Demo {
// 初始化属性
instanceProperty = 'instanceProperty'
instanceFunction = () => {
return this.instanceProperty
}
// 静态属性
static staticProperty = 'staticProperty'
static staticFunction = function () {
return Demo.staticProperty
}
}
const demo = new Demo()
console.log(demo.__proto__.instanceFunction) // undefined
console.log(demo.instanceFunction.call()) // instanceProperty
console.log(Demo.staticFunction()) // staticProperty
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- @babel/plugin-proposal-decorators:支持装饰器 使用:
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }]
]
2
3
function dec(id) {
console.log('evaluated', id)
return (target, property, descriptor) => console.log('executed', id)
}
class Example {
@dec(1)
@dec(2)
method() {}
}
2
3
4
5
6
7
8
9
10
- @babel/plugin-transform-runtime:作用是将 helper 和 polyfill 都改为从一个统一的地方引入,并且引入的对象和全局变量是完全隔离的,将所有的辅助函数集中到一个模块中,避免所有 js 文件中有重复的辅助函数声明
# 如何使用插件
babel.config.json 添加:
{
"plugins": ["babel-plugin-myPlugin", "@babel/plugin-transform-runtime"]
}
2
3
# 插件顺序
- 插件在 Presets 前运行。
- 插件顺序从前往后排列。
- Preset 顺序是颠倒的(从后往前)。
例如:
{
"plugins": ["transform-decorators-legacy", "transform-class-properties"]
}
2
3
先执行 transform-decorators-legacy ,在执行 transform-class-properties。
重要的时,preset 的顺序是 颠倒的。如下设置:
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
2
3
将按如下顺序执行: 首先是 @babel/preset-react,然后是 @babel/preset-env。
# 插件参数
和 preset 的格式一样
{
"plugins": [
"pluginA", // 使用字符串
["pluginA"], // 使用数组
["pluginA", {}] // 使用元组(tuple)第一个参数为插件名称,第二为插件参数的对象
]
}
2
3
4
5
6
7