本文将系统梳理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.jsbabel.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-envbabel-node babel-core babel-cli babel-preset-envbabel6 babel-polyfill 对应 babel7的@babel/polyfill,但是babel从7.4.0开始,不推荐使用此软件包,而直接包括core-jsbabel.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-classesjavascript{
- "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 typesbabel的优势
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 许可协议。转载请注明出处!