♠ Babel的深入解析
Babel的深入解析
为什么需要babel?
事实上,在开发中我们很少直接去接触babel,但是babel对于前端开发来说,目前是不可缺少的一部分:开发中,我们想要使用ES6+的语法,想要使用TypeScript,开发React项目,它们都是离不开Babel的;
Babel是一个工具链,主要用于旧浏览器或者缓解中将ECMAScript 2015+代码转换为向后兼容版本的JavaScript;
包括:语法转换、源代码转换、Polyfill实现目标缓解缺少的功能等;
babel本身可以作为一个独立的工具(和postcss一样),不和webpack等构建工具配置来单独使用。
如果我们希望在命令行尝试使用babel,需要安装如下库:
- @babel/core:babel的核心代码,必须安装;
- @babel/cli:可以让我们在命令行使用babel;
使用babel来处理我们的源代码:
- src:是源文件的目录;
- --out-dir:指定要输出的文件夹dist;
插件的使用
比如我们需要转换箭头函数,那么我们就可以使用箭头函数转换相关的插件:
npm install @babel/plugin-transform-arrow-functions -D
npx babel src --out-dir dist --plugins=@babel/plugin-transform-arrow-functions
查看转换后的结果:我们会发现 const 并没有转成 var
- 这是因为 plugin-transform-arrow-functions,并没有提供这样的功能;
- 我们需要使用 plugin-transform-block-scoping 来完成这样的功能;
npm install @babel/plugin-transform-block-scoping -D
npx babel src --out-dir dist --plugins=@babel/plugin-transform-block-scoping,@babel/plugin-transform-arrow-functions
Babel的预设preset
但是如果要转换的内容过多,一个个设置是比较麻烦的,我们可以使用预设(preset):
npm install @babel/preset-env -D
执行如下命令:
Babel的底层原理
babel是如何做到将我们的一段代码(ES6、TypeScript、React)转成另外一段代码(ES5)的呢?
从一种源代码(原生语言)转换成另一种源代码(目标语言),这是什么的工作呢?就是编译器,事实上我们可以将babel看成就是一个编译器。Babel编译器的作用就是将我们的源代码,转换成浏览器可以直接识别的另外一段源代码;
Babel也拥有编译器的工作流程:
- 解析阶段(Parsing)
- 转换阶段(Transformation)
- 生成阶段(Code Generation)
当然,这只是一个简化版的编译器工具流程,在每个阶段又会有自己具体的工作:
babel-loader
在实际开发中,我们通常会在构建工具中通过配置babel来对其进行使用的,比如在webpack中。
npm install babel-loader @babel/core
我们可以设置一个规则,在加载js文件时,使用我们的babel
我们必须指定使用的插件才会生效
如果我们一个个去安装使用插件,那么需要手动来管理大量的babel插件,我们可以直接给webpack提供一个preset,webpack会根据我们的预设来加载对应的插件列表,并且将其传递给babel
比如常见的预设有三个:
- env
- react
- TypeScript
安装preset-env:npm install @babel/preset-env
我们最终打包的JavaScript代码,是需要跑在目标浏览器上的,那么如何告知babel我们的目标浏览器呢?
- rowserslist工具
- target属性
之前我们项目中已经使用了browserslist工具,我们可以对比一下不同的配置,打包的区别:
我们也可以通过targets来进行配置
那么,如果两个同时配置了,哪一个会生效呢?
配置的targets属性会覆盖browserslist;但是在开发中,更推荐通过browserslist来配置,因为类似于postcss工具,也会使用browserslist,进行统一浏览器的适配;
Stage-X的preset
要了解Stage-X,我们需要先了解一下TC39的组织:
- TC39是指技术委员会(Technical Committee)第 39 号;
- 它是 ECMA 的一部分,ECMA 是 “ECMAScript” 规范下的 JavaScript 语言标准化的机构;
- ECMAScript 规范定义了 JavaScript 如何一步一步的进化、发展;
TC39 遵循的原则是:分阶段加入不同的语言特性,新流程涉及四个不同的 Stage
- Stage 0:strawman(稻草人),任何尚未提交作为正式提案的讨论、想法变更或者补充都被认为是第 0 阶段的"稻草人";
- Stage 1:proposal(提议),提案已经被正式化,并期望解决此问题,还需要观察与其他提案的相互影响;
- Stage 2:draft(草稿),Stage 2 的提案应提供规范初稿、草稿。此时,语言的实现者开始观察 runtime 的具体实现是否合理;
- Stage 3:candidate(候补),Stage 3 提案是建议的候选提案。在这个高级阶段,规范的编辑人员和评审人员必须在最终规范上签字。Stage 3 的提案不会有太大的改变,在对外发布之前只是修正一些问题;
- Stage 4:finished(完成),进入 Stage 4 的提案将包含在 ECMAScript 的下一个修订版中;
在babel7之前(比如babel6中),我们会经常看到这种设置方式:
它表达的含义是使用对应的 babel-preset-stage-x 预设;但是从babel7开始,已经不建议使用了,建议使用preset-env来设置;
Babel的配置文件
像之前一样,我们可以将babel的配置信息放到一个独立的文件中,babel给我们提供了两种配置文件的编写:
- babel.config.json(或者.js,.cjs,.mjs)文件;
- .babelrc.json(或者.babelrc,.js,.cjs,.mjs)文件;
认识polyfill
为什么时候会用到polyfill呢?
- 比如我们使用了一些语法特性(例如:Promise, Generator, Symbol等以及实例方法例如Array.prototype.includes等)
- 但是某些浏览器压根不认识这些特性,必然会报错;
- 我们可以使用polyfill来填充或者说打一个补丁,那么就会包含该特性了;
babel7.4.0之前,可以使用 @babel/polyfill的包,但是该包现在已经不推荐使用了:
babel7.4.0之后,可以通过单独引入core-js和regenerator-runtime来完成polyfill的使用:npm install core-js regenerator-runtime --save