什么是babel
官网:javascript编译器。最初从 6to5
库发展而来。
效果:
- 将es6语法,转换成es5语法。比如将箭头函数,转化成普通函数。
- 默认只转换语法,不转换API,比如 Iterator、Set、Map、Reflect
过程:解析(parsing)、转换(transforming)和生成(generating)
核心组成
@babel/cli
:语法转换的核心依赖包,它本身不具备转换处理能力,只提供上层的API。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17var babelCore = require("@babel/core");
var sourceCode = `let fn = (num) => num + 1`;
var options = {
code: true, //是否生成解析的代码
ast: true, //是否生成抽象语法树
sourceMaps: true, //是否生成sourceMap
plugins: [], // 无插件
presets: [], // 无preset
};
babelCore.transform(sourceCode, options, function (err, result) {
console.log(sourceCode);
console.log(result.code);
console.log(result.map);
console.log(result.ast);
});转换功能被拆分到各个插件中,比如
@babel/plugin-transform-react-jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27// 转换react.js代码
const fs = require('fs')
const babel = require("@babel/core")
/* 第一步:模拟读取文件内容。 */
fs.readFile('./element.js',(e,data)=>{
const code = data.toString('utf-8')
/* 新版转换 jsx 文件:转换后为 jsx(xxx, xxx) 这种 */
const result1 = babel.transformSync(code, {
plugins: [
[
"@babel/plugin-transform-react-jsx", {
"runtime": "automatic"
}
]
],
});
/* 第三步:输出转换后的内容 */
console.log(result1.code)
console.log('-'.repeat(40))
/* 老版转换 jsx 文件:转换后为 React.createElement 这种 */
const result2 = babel.transformSync(code, {
plugins: ["@babel/plugin-transform-react-jsx"],
});
/* 第三步:输出转换后的内容 */
console.log(result2.code)
})@babel/parser
:将源码解析成AST@babel/generator
:将转换好的AST生成新代码。@babel/cli
:内置babel的CLI命令1
2npm install --g @babel/cli
babel input.js -o output.js
配置文件
- 位置:package.json,.babelrc,.babelrc.js,babel.config.js 都可
- 常用配置:可以参考前面代码中的
babelCore.transform
函数的入参
插件系统(plugins)
Plugin类型:
- 语法插件:
- 阶段:解析(parsing)
- 作用:不具备任何功能性,只是将对应语法的解析功能打开。解析语法的功能均在
@babel/parser
中实现。 - 命名规则:以
@babel/plugin-syntax
开头,比如@babel/plugin-syntax-typescript
、@babel/plugin-syntax-jsx
- 转换插件:
- 阶段:转换(transforming)
- 作用:将原代码的AST转成目标代码的AST
- 命名规则:
- 以
@babel/plugin-transform
开头:正式转换插件。比如@babel/plugin-transform-runtime
- 以
@babel/plugin-proposal
开头:提案转换插件。比如@babel/plugin-proposal-class-properties
- 以
预设系统(presets)
一个个去配置各种插件,太过麻烦,浪费精力。因此,出现了 presets 的概念,它可以理解成「插件套餐」,每一个preset内部,都组合搭配了多个插件供开发者使用。
语法转换(**@babel/preset-env
**)
作用:
一个智能预设,能直接通过指定浏览器版本,来将代码语法降到对应浏览器支持的写法。
默认情况下,@babel/env等于@babel/preset-es2015、@babel/preset-es2016和@babel/preset-es2017三个套餐的叠加
写法:
1 | { |
API转换
@transform/polyfill
vs @transform/runtime
- polyfill:改造目标浏览器,让你的浏览器拥有本来不支持的特性
- runtime:改造你的代码,让你的代码能在所有目标浏览器上运行,但不改造浏览器
举例:
在IE中,如果引入的是polyfill,那么打开控制执行 Reflect.ownKeys
是可以的;如果引入的是runtime,会报错。
分析:
- polyfill:修改的是全局对象。如果检测到不存在,会修改
window
的属性。 - runtime:将你的代码中对新API的使用,换个新名字,然后实现这个函数。比如
Refelct.ownKeys
可能会被换成_es6_reflect_ownkeys
,并且实现_es6_reflect_ownkeys
polyfill 方案
安装:
1 | npm install --save-dev @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-runtime |
配置:
1 | { |
总结:
- 不推荐。会修改全局对象,不如 runtime-corejs3 方案。
- 不再使用
@babel/polyfill
,而是根据需要安装core-js@3
和regenerator-runtime
- core-js@3:处理Array.from、Object.assign 等方法
- regenerator-runtime:处理生成器函数(function *)
- 使用
@babel/runtime
,避免重复生成babel的「辅助函数」,减少生成代码体积,比如_classCallCheck
、_createClass
等
@babel/runtime
要配合@babel/plugin-transform-runtime
使用。此时,@babel/plugin-transform-runtime
的作用是是移除辅助函数,将其替换为@babel/runtime/helpers
中函数的引用
runtime-corejs3 方案(推荐)
安装:
1 | npm install --save-dev @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-runtime |
配置:
1 | { |
总结:
- 推荐,不会污染全局变量
@babel/runtime-corejs3
相当于@babel/runtime
加上不污染全局的core-jASTs@3
babel实战
AST实战:
babel配置:
sourceType
设置为unambiguous
:因为babel插件依赖当前文档类型,来决定是import还是require调用,因此需要设置成它。可以参考:https://blog.liuyunzhuge.com/2019/10/11/babel详解(六)-options/ 、https://stackoverflow.com/questions/52407499/how-do-i-use-babels-usebuiltins-usage-option-on-the-vendors-bundle
第三方依赖:
- 背景:在对项目的js代码降级时,除了项目本身的代码,有时还需要对第三方库依赖库的代码进行降级。
- 方案:
- 通过 babel 的
include
属性,声明需要被额外转译的第三方库依赖库。配合sourceType
设置为unambiguous
避免转移解析过程出错。 - 公司内部提供了一种
degradation
的能力,它能一次性扫描出不符合当前版本要求的第三方库依赖库,并且将其生成一个配置文件。当babel编译时,自动将配置文件的内容,添加到include
中。
- 通过 babel 的