一、 关于Babel
Babel 是一个JavaScript编译器
Babel 主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
babel 有3种使用方式: 命令行(cli);构建工具的插件 (webpack 的 @babel/loader);单体文件 (standalone script)
3种使用方式只是入口不同,其核心都相同, 即包括3个步骤:
babel 总共分为3个阶段:解析,转换,生成。
- 解析阶段: @babel/parser 将源码解析成AST
- 转换阶段: @babel/core本身不具有任何转化功能,它把转化的功能都分解到一个个 plugin 里面
- 生成阶段:@babel/generator将转好的AST重新生成代码
常说的 plugins & presets 的配置 就是 使用babel时主要的配置参数,如下:使用babel工具可以在3处配置: .babelrc/.babelrc.js 、package.json 的 babel 、babel.config.js
另外需要注意的是: babel 转换只能处理语法,不能处理新增的API。对于API的处理需要使用polyfill。
二、Plugins
babel 本身不具有任何转化功能
,它把转化的功能都分解到一个个 plugin 里面。因此当我们不配置任何插件时,经过 babel 的代码和输入是相同的:
一个插件一般支持一个语法的转化(插件是小型的 JavaScript 程序), 如添加插件@babel/plugin-transform-arrow-functions
, 然后使用@babel/cli 编译:
npm install --save-dev @babel/plugin-transform-arrow-functions
npx babel src --out-dir dir --plugins=@babel/plugin-transform-arrow-functions
一个插件支持一个语法。对于一套规范(比如 es2015 )包含大概十几二十个转译插件。如果每次要开发者一个个添加并安装,配置文件很长不说,npm install 的时间也会很长,更不谈我们可能还要同时使用其他规范。 所以 就有了插件套餐preset。
各规范下的插件列表: https://www.babeljs.cn/docs/plugins-list
三、Presets
安装@babel/preset-env, 使用@babel/cli 编译:
npm install --save-dev @babel/preset-env
npx babel src --out-dir dir --presets=@babel/env
官方提供的Presets包:
- @babel/preset-env for compiling ES2015+ syntax
- @babel/preset-typescript for TypeScript
- @babel/preset-react for React
- @babel/preset-flow for Flow
其中最重要的需要关注的是 @babel/preset-env
四、Polyfill
babel 默认只转换 js 语法,而不转换新的 API; 对于新API 需使用Polyfill
用法1: 在所有代码运行之前增加 require(‘babel-polyfill’)
用法2: webpack.config.js 中将 @babel/polyfill 作为第一个 entry
注意:必须把 @babel/polyfill 作为 dependencies 而不是 devDependencies
使用@babel/polyfill 存在的问题:
1、@babel/polyfill 会导致打出来的包非常大,因为@babel/polyfill 是一个整体,把所有方法都加到原型链上。这个问题可以通过单独使用 core-js 的某个类库来解决,core-js 都是分开的。
2、@babel/polyfill 会污染全局变量,给很多类的原型链上都作了修改
babel7 后推荐使用 core-js/stable 和 regenerator-runtime/runtime
或者使用@babel/preset-env 中的useBuiltIns 配置
五、最重要的Preset @babel/preset-env
preset-env配置:
{
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"useBuiltIns": "usage",
"corejs": 3
}
]
]
}
module:用来设置是否把ES6的模块化语法改成其它模块化语法,默认为 auto,也就是会将ESM转为CJS。项目配置设置module为false也就是仍使用ESM,这样在webpack打包时可以使用 tree-shaking 去除无用代码减小包体积。
useBuiltIns:该配置项的值有 entry、usage、false。该配置主要和polyfill的行为相关。
值为false。preset-env不做额外处理,需要在入口文件引入 @babel/polyfill(全量)
-
值为entry。在入口文件引入 @babel/polyfill,转码时会根据 browerlist 引入polyfill。
-
值为usage。不用再显示引入 polyfill文件,转码时会根据 browerlist 和 编写的代码按需引入polyfill。
六、其他重要的包
@babel/cli: 命令行中使用, 如上面的使用方式
-
@babel/node : 命令行中直接执行+转译node文件,@babel/node = @babel/register + @babel/polyfill。 即 可以直接 @babel/node xxx.js, 如图所示:
@babel/register : 用法: require("@babel/register"); node 后续运行时所需要 require 进来的扩展名为 .es6、.es、.jsx、 .mjs 和 .js 的文件将由 Babel 自动转换。实时转码,所以 只适合在开发环境使用。
-
@babel/runtime
语法转换时, 创建了helper函数。而在实际项目中我们的js文件很多,在语法转换过程中每个文件都会创建很多这种helper函数,这样会导致最终生成的包体积十分臃肿。
这个时候 @babel/runtime 上场了,涉及到语法转换的helper函数都打包到 @babel/runtime 中,所以在语法转换的时候不用重复创建,只要引入 @babel/runtime 内的模块就可以了。
但是打包的文件那么多,总不可能每个都手动修改吧。 这时候就需要@babel/plugin-transform-runtime 了。
运行环境使用, 放在 dependencies中 而不是devDe..
-
@babel/plugin-transform-runtime
@babel/plugin-transform-runtime 这个包的作用就是移除语法转换生成的helper函数,然后使用 @babel/runtime 中的辅助函数来替换。这样就避免了我们手动替换也减小了包的体积。
@babel/loader
与webpack等构建工具交互,可在webpack中配置,优先级最高@babel/parser
原babel 解析语法的内核 babylon@babel/core
babel 核心功能, 执行下面代码:
var babelCore = require("@babel/core");
var sourceCode = `let fn = (num) => num + 2`;
var options = {
//是否生成解析的代码
code: true,
//是否生成抽象语法树
ast: true,
//是否生成sourceMap
sourceMaps: true,
plugins: [],
presets: [],
};
babelCore.transform(sourceCode, options, function (err, result) {
console.log(sourceCode);
console.log(result.code);
console.log(result.map);
console.log(result.ast);
});
结果:七、思维导图
我将上面的思路整理成了思维导图,方便记忆。 自行取用。
参考文档:
1、 一口(很长的)气了解 babel
2、一文彻底读懂Babel
hi~~ 学废了吗~