JS代码编译—Babel
JS代码编译—Babel
什么是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 17
var 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-jsx1 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 2
npm 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
2
3
4
5
6
7
8
9
10
11
12
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 Chrome versions", // 兼容最新2个版本的chrome
"useBuiltIns": "entry", // 是否引入polyfill,可以配置三个值:false(不引入)、usage(按需引入)和entry(项目入口处引入)
"corejs": "3.22" // 引入哪个版本的core-js,可以选择2(默认)或者3,只有当useBuiltIns不为false时才会生效
}
]
]
}
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
2
npm install --save-dev @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-runtime
npm install --save @babel/runtime core-js@3
配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"presets": [
[
"@babel/preset-env", {
"useBuiltIns": "usage",
"corejs": 3
}
]
],
"plugins": [
["@babel/plugin-transform-runtime"]
]
}
总结:
- 不推荐。会修改全局对象,不如 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
2
npm install --save-dev @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-runtime
npm install --save @babel/runtime-corejs3
配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"presets": [
[
"@babel/preset-env" // 不再使用polyfill,无需配置 useBuiltIns & corejs ,
]
],
"plugins": [
[
"@babel/plugin-transform-runtime", {
"corejs": 3
}
]
]
}
总结:
- 推荐,不会污染全局变量
@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 的
参考资料
本文由作者按照 CC BY 4.0 进行授权

