本文将系统梳理Babel
相关知识。包括Babel
的基本概念,配置文件种类,应用和类库的配置案例讲解,Babel7的主要变更,browserslist配置,编译TS代码适用tsc还是babel等内容。
Babel
是一个JavaScript
编译器Babel
是一个工具链,主要用于将ECMAScript 2015+
版本的代码转换为向后兼容的 JavaScript
语法,以便能够运行在当前和旧版本的浏览器或其他环境中。注:ECMAScript
是规格,JS是其规格的实现,不仅包含ECMAScript
,还包含浏览器下的JS。如果想给浏览器下的JS打补丁要靠其他工具了(比如observe-webpack-plugin
)。
pkg.babel
字段.babelrc.json
(或者.babelrc
,.js
,.cjs
,.mjs
)文件
babel6
可以在.babelrc
引用 .babelrc.js
, babel7
不允许babel.config.json
(或者.js
,.cjs
,.mjs
)文件; 7.x
新增的.babelrc
与.babelrc.json
及 .babelrc.js
不能同时使用,但babel6中可以在.babelrc
引入.babelrc.js
babel.config.json
与 babel.config.js
也不能同时使用.babelrc.js
与 babel.config.js
可以共存,.babelrc
会merge到babel.config.js
的配置merge({babel.config}, {babelrc}, {rullup/gulp/webpack中的配置})
plugin
babel
的插件翻译某一块功能preset
babel
插件集合的预设,包含某一部分的插件plugins
先于presets
之前执行,plugins
从前到后执行,presets
从后往前执行Babel默认只转换新的JavaScript句法,而不转换API,比如Iterator
、Generator
、Set
、Maps
、Proxy
、Reflect
、Symbol
、Promise
等全局对象,以及一些定义在全局对象上的方法(比如Object.assign
)都不会转码。
preset-env
的配置useBuiltIns
字段
false
不引用polyfill
(默认值)entry
整体导入补丁usage
按需导入补丁useBuiltIns
为 entry
或 usage
的时候,需要安装 core-js@2
或者 core-js@3
babel version < 6
项目入口引入 babel-polyfill
>= 7 < 7.4
项目入口引入 @babel/polyfill
> 7.4
配置@babel/preset-env
注: polyfill
依赖 corejs
core-js@3
, 因为corejs@2
已经被冻结好几年了,所有新功能仅添加到corejs@3
,包括提案corejs@3
也删除了一些过时的功能,不再有非标准非提议功能corejs@3
为浏览器错误/问题添加了许多修复程序。例如,已修复 Safari 12.0 Array.prototype.reverse
错误。corejs组成
core-js
,它定义了全局 polyfill
。 (~500KB,40KB 压缩和压缩)core-js-pure
,提供polyfills
而不污染全局环境。它相当于 core-js@2
中的 core-js/library
。 (~440KB)core-js-bundle
:定义全局 polyfill
的 core-js
的捆绑版本。ecma提案的4个阶段
Stage 0
- 稻草人(strawman): 只是一个想法,经过 TC39 成员提出即可。Stage 1
- 提案(proposal): 初步尝试。Stage 2
- 初稿(draft): 完成初步规范。Stage 3
- 候选(candidate): 完成规范和浏览器初步实现。javascriptmodule.exports = {
presets: [
[
"@babel/preset-env",
{
// 推荐使用 .browserslistrc文件,因为该文件已被生态系统中的许多工具所使用,例如postcss-preset-env,stylelint,eslint-plugin-compat等
// 写在这里(babe配置里),是为了方便演示
"targets": "> 0.25%, not dead", // 仅包括浏览器市场份额超过0.25%的用户所需的polyfill和代码转换(忽略没有安全更新的浏览器,如IE 10和BlackBerry)
// "targets": { // 有这么多选项 chrome,opera,edge,firefox,safari,ie,ios,android,node,electron
// "edge": "17",
// "firefox": "60",
// "chrome": "67",
// "safari": "11.1",
// },
// targets 如果未指定目标,则默认转换所有ECMAScript 2015+代码
// 还可以写在package.json文件里面 "browserslist": "> 0.25%, not dead"
// useBuiltIns false-不引入polyfill(默认值) entry-入口引入(整体引入) usage-按需引入
"useBuiltIns": "usage",
// "corejs": 3 // useBuiltIns为entry或usage的情况下需要配置这个选项
"corejs": {
version: "3.24",
proposals: true
}
// useBuiltIns为entry要在入口文件引入 `import "core-js"`
// 如果corejs@3 @babel/preset-env还会优化,只导入针对目标浏览器所需要的全部补丁
}
],
// 从 v7.0.0-beta.55 开始废弃了,需要用户自己配置plugins
// 废弃原因 提案不稳定 理由是 https://segmentfault.com/a/1190000018358854
// "@babel/preset-stage-1"
],
// babel7 提案句法 转换需要自己手动配置,可在 node_modules/@babel/preset-stage-0 查看一下 内容不多
plugins: [
"@babel/plugin-proposal-do-expressions",
// 编译多个文件时,如果不想这些公共辅助函数在每个文件都注入一遍,就需要这个插件配合。
"@babel/plugin-transform-runtime"
]
}
类库的配置
jsmodule.exports = {
presets: ['@babel/preset-env'],
plugins: [
[
'@babel/plugin-transform-runtime',
{
corejs: 3 // cnpm i -S @babel/runtime-corejs3
}
]
]
};
babel
中有个概念, es6+ 分为 句法
(新语法)和 API
两个概念。
一般对于项目来说, API
的转换我们通过配置@babel/preset-env
的useBuiltIns
字段为usage
或entry
。
但是该方式不适用于类库,因为会造成污染。(类库的垫片与应用的垫片功能可能存在差异)。
打包类库,我们通常使用runtime
方案(为es6 API创建一个沙盒环境)。
rumtime
方案如下:
@babel/runtime
抽离公共辅助函数
async
、...
class
等转成es5需要一些辅助函数,该插件会将辅助函数注入到打包文件中。@babel/runtime-corejs3
core-js
是直接补全浏览器的es6+API
,会带来环境污染问题,该包提供一种fuction引用的方式来避免es6+ API污染全局环境@babel/plugin-transform-runtime
编译多个文件,如果不想这些公共辅助函数在每个文件都注入一遍,就需要这个插件配合。(应用也需要这个插件) 该包依赖上述两个包参考资料: 一文搞懂 core-js@3、@babel/polyfill、@babel/runtime、@babel/runtime-corejs3 的作用与区别
注意: 因为babel
并不处理模块化,所以构建后的文件polyfill
或runtime
仍需要在特定的环境(node)下运行,想要在其他环境(浏览器)中运行还需要借助模块化工具(如webpack)
@babel/node
@babel/core @babel/cli
@babel/preset-env
babel-node
babel-core
babel-cli
babel-preset-env
babel6
babel-polyfill
对应 babel7的@babel/polyfill
,但是babel从7.4.0开始,不推荐使用此软件包,而直接包括core-js
babel.config.js
babel.config.json
babel.config.js
局部配置 .babelrc
用于monorepo
项目babel.config.js
可以定义全局配置的作用范围以及局部配置的开启关闭preset
而是手动配置plugin
babel-preset-stage-x
babel7废除了这些预设,需要手动引入plugin,预设语法并不多可以在node_modules/@babel/preset-stage-0
看一下plugin
更名
-proposal
,如@babel/plugin-transform-function-bind
现在@babel/plugin-proposal-function-bind
@babel/plugin-transform-es2015-classes
成了@babel/plugin-transform-classes
javascript{
- "presets": ["@babel/preset-react"]
+ "presets": ["@babel/preset-react", "@babel/preset-flow"] // parse & remove flow types
+ "presets": ["@babel/preset-react", "@babel/preset-typescript"] // parse & remove typescript types
}
以上内容个人写了一些案例,如有需要请移步
browserslist
可以在构建工具(Webpack
/babel
/...)、.browserslist文件
、pkg.browserslist
中配置
javascript// 1. 在构建工具如webpack中配置
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
// use: 'babel-loader', // 走babel配置
use: { // babel配置写在这里也可以, 如果项目根目录下还有配置文件如babel.config.js会把这里的配置项merge到配置文件里面
loader: 'babel-loader',
options: {
presets: [['@babel/parset-env', {target: '这里配置'}]],
}
},
}
]
},
}
// 2. 在babel中配置如 babal.config.js
module.exports = {
presets: [
['@babel/env', {
"useBuiltIns": "usage",
"corejs": 3,
}]
]
}
// 3. browserslist配置
// 如果 前一步没有配置parset-env的target字段,
// 再会寻找 .browserlists文件 或pkg.browserlists字段
/* pkg.browserlists 移动端配置示例
"browserslist": [
"Android >= 4.4",
"ios >= 10",
"> 0.5%"
]
android 4.4以上browserlist配置参考这篇文章
https://www.yuque.com/guojw/fe-project/rg6nsk#Rd7ho
*/
npx browserlists
通过控制变量法,检测是否生效postcss
同样支持browserlists
,(如postcss-presets-env
,想要看到flex前缀,要调整到android 4.0
)查看各浏览器及各版本市场份额 点这里
tsc的编译流程
babel的编译流程
babel
对比tsc
的编译流程,差别并不大
Parser
对应 tsc 的 Scanner
和 Parser
,都是做词法分析和语法分析,只不过 babel
没有细分。Transform
阶段做语义分析和代码转换,对应 tsc 的 Binder
和 Transformer
。只不过 babel
不会做类型检查,没有 Checker
。Generator
做目标代码和 sourcemap
的生成,对应 tsc
的 Emitter
。只不过因为没有类型信息,不会生成 d.ts
。babel
的劣势(@babel/preset-typescript
)
.d.ts
文件
npx tsc --declaration -p ./ -t esnext --emitDeclarationOnly --outDir types
babel
的优势
babel
能根据目标浏览器转译指定语法;而ts
只能配置指定的ecma
版本polyfill
(es6API) Babel
能够根据目标环境自动添加 polyfill
;TS
编译器则不处理API需要手动引入corejs
或runtime
(致命缺点)ts
只支持最新的es
规范,和部分提案,想要支持新语法就要升级ts
版本;babel
通过插件支持es
句法 API
及 提案综上: babel
支持ts
是最好的选择,@babel/preset-typescript
也是ts
开发团队的人实现的
参考资料:
请移步这里 《抽象语法树AST》
本文作者:郭敬文
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!