一文看懂 babel - 为何诞生、做了什么、怎么做的

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: babel 在前端快速发展的最近几年,为前端的工程化提供了莫大的帮助,解决了前端各种浏览器兼容问题导致的 js 崩溃,让我们可以放下的用上新的各种 es6、es7 等新语法,今天聊一聊 babel 的工作原理。

网络异常,图片无法展示
|

babel 在前端快速发展的最近几年,为前端的工程化提供了莫大的帮助,解决了前端各种浏览器兼容问题导致的 js 崩溃,让我们可以放下的用上新的各种 es6es7 等新语法,今天聊一聊 babel 的工作原理。

babel 为何而诞生

聊之前我们先讲讲 babel 为何而诞生,浏览器的生态环境大家都清楚,特别是在国内,哪怕是微软已经给 IE 判了死刑的现在,依旧还是丢不掉的包袱。对于 web 应用而言,css 的兼容可以说一定程度上会自行“优雅降级”,大部分情况下只会影响到页面的展示,但是 js 的兼容却很容易导致程序的崩溃和线上的问题。babel 也应运而生,特别是在 es6 出现后,一下子出现了一大批好用的语法:letconstPromiseClass、扩展运算符等等,让人眼馋不已,也让 babel 逐渐在前端生态圈中变得不可或缺。

babel 做了什么

我们先看看 babel 为了让我们能够不再关心 JS 的语法兼容而做了什么:

  • 语法检查
  • 语法转译
  • polyfill 导入

语法检查

首先 babel 会按照自己当前的版本和配置,对你的代码进行解析,如果你使用了不支持的语法,或者语法错误他都会报错,从而避免未支持的对应语法被丢上生产,或者一些降级后可能没有报错的代码在编译时能够及时发现,比如 const 重复赋值等。

语法转译

语法转译即 babel 会按照你所配置的目标环境,将语法转进行译降级到目标环境可以解析的语法。

Classconstletasyncawait,如果你的目标环境都支持,那它会保持这些语法原封不动,而如果你的目标环境中存在不兼容的,那它就会对语法做降级处理,如 Class 将会按照组合继承的方式进行转译(如果有继承的话),比如以下这段代码:

class Z {
    dd = () => {
        console.log('dd');
    };
}
class A extends Z {
    constructor() {
        console.log('hello');
    }
    do = () => {
        console.log('do');
    };
}
复制代码

将会被解析成为这样(删减后):

'use strict';
function _inherits(subClass, superClass) {
    // ...
}
function _createSuper(Derived) {
    var hasNativeReflectConstruct = _isNativeReflectConstruct();
    return function _createSuperInternal() {
        // ...
        return _possibleConstructorReturn(this, result);
    };
}
function _possibleConstructorReturn(self, call) {
    // ...
    return _assertThisInitialized(self);
}
function _createClass(Constructor, protoProps, staticProps) {
    // ...
}
function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError('Cannot call a class as a function');
    }
}
function _defineProperty(obj, key, value) {
    // ...
}
var Z = /*#__PURE__*/ _createClass(function Z() {
    _classCallCheck(this, Z);
    _defineProperty(this, 'dd', function () {
        console.log('dd');
    });
});
var A = /*#__PURE__*/ (function (_Z) {
    _inherits(A, _Z);
    var _super = _createSuper(A);
    function A() {
        var _this;
        _classCallCheck(this, A);
        console.log('hello');
        return _possibleConstructorReturn(_this);
    }
    return _createClass(A);
})(Z);
复制代码

letconst 则可能会被转译为 var,数组扩展符被解析成 concat 等,这样便可保证代码在目标环境中的安全运行,再配合语法检查这套组合拳,便可让开发者放下的使用新语法。

polyfill 导入

babel 通过语法检查和转译,已经解决了新语法的问题,但是这还没结束,除了新语法外,在浏览器上运行还存在一个障碍,就是各种新的方法和对象,如 PromiseArray.from 等,这些 API 需要通过导入 polyfill 来解决,早期的 babel 采用 babel-polyfill 直接将所有的 polyfill 进行打包,然后开发者直接引入全量的 polyfill 包,然后随着发展,该方案逐渐被 core-js 所取代,core-js 除了支持导入全量的 polyfill 外,还支持按需导入,比如这样的一段代码:

Array.from(new Set([1, 2, 3, 2, 1]));
复制代码

如果 babel 发现你的目标环境不支持 Array.from 时,他将会把代码转换成这样:

'use strict';
require('core-js/modules/es.array.from.js');
require('core-js/modules/es.string.iterator.js');
require('core-js/modules/es.array.iterator.js');
require('core-js/modules/es.object.to-string.js');
require('core-js/modules/es.set.js');
require('core-js/modules/web.dom-collections.iterator.js');
Array.from(new Set([1, 2, 3, 2, 1]));
复制代码

这样 babel 就能按照你的代码所缺少的环境依赖按需导入对应的 polyfill 了,甚至通过配置他还能做到按需导入且不会影响全局环境:

'use strict';
var _interopRequireDefault = require('@babel/runtime-corejs3/helpers/interopRequireDefault');
var _from = _interopRequireDefault(require('@babel/runtime-corejs3/core-js-stable/array/from'));
var _set = _interopRequireDefault(require('@babel/runtime-corejs3/core-js-stable/set'));
(0, _from.default)(new _set.default([1, 2, 3, 2, 1]));
复制代码

这样 babel 就会将相应的 polyfill 作为 lib 导入并使用,而不是直接注入到环境中。

babel 怎么做到的

那么 babel 是如何做到这三件事情的呢,其实最主要靠的就是 AST

简单的说,babel 会将你的文件解析成 AST 也就是抽象语法树,然后它会按照你的配置,遍历相应的节点。比如如果需要转译 letconst,那它就会将 AST 中的 letconst 语法块变更为 var 语法块。而语法检查则是在解析成 AST 这一步所做的,对于不认识的语法,它将无法解析成功,就会报错。而 polyfill 其实也是相同的原理,babel 会遍历整个语法树,找出目标环境不支持的方法和 API,然后将其对应的 polyfill 模块进行导入,如果是配置了不影响全局的情况下还会对对应的语法块做转译。

总结

babel 通过 AST,解决了困扰着前端届的 JS 兼容问题,一定程度上已经成为了前端生态的基石。了解 babel 为何出现、做了什么、怎么做的可以帮助我们更好的了解自己的程序是如何运行的。

相关文章
|
6月前
|
JavaScript 前端开发 编译器
js开发: 请解释什么是Babel,以及它在项目中的作用。
**Babel是JavaScript编译器,将ES6+代码转为旧版JS以保证兼容性。它用于前端项目,功能包括语法转换、插件扩展、灵活配置和丰富的生态系统。Babel确保新特性的使用而不牺牲浏览器支持。** ```markdown - Babel: JavaScript编译器,转化ES6+到兼容旧环境的JS - 保障新语法在不同浏览器的运行 - 支持插件,扩展编译功能 - 灵活配置,适应项目需求 - 富强的生态系统,多样化开发需求 ```
55 4
|
资源调度 JavaScript 前端开发
探索Babel:现代JavaScript开发中的编译利器
Babel是一个流行的JavaScript编译工具,用于将新的ECMAScript标准(如ES6、ES7等)转换为向后兼容的JavaScript版本,以便在不同浏览器和环境中运行。它在现代JavaScript开发中扮演着关键角色,帮助开发者编写可读性强、高效且兼容性良好的代码。在本博客中,我们将深入研究Babel的核心概念、配置、插件和预设,以帮助您更好地了解这个强大的工具。
66 0
|
2月前
|
JavaScript
Nest.js 实战 (十一):配置热重载 HMR 给服务提提速
这篇文章介绍了Nest.js服务在应用程序引导过程中,TypeScript编译对效率的影响,以及如何通过使用webpackHMR来降低应用实例化的时间。文章包含具体教程,指导读者如何在项目中安装依赖包,并在根目录下新增webpack配置文件webpack-hmr.config.js来调整HMR相关的配置。最后,文章总结了如何通过自定义webpack配置来更好地控制HMR行为。
|
2月前
|
资源调度 前端开发 JavaScript
PostCSS的安装与应用技术全解
PostCSS通过其插件系统提供了极大的灵活性和强大的CSS处理能力。无论是自动化添加浏览器前缀、使用未来的CSS特性还是优化CSS文件大小,PostCSS都能在现代前端开发流程中发挥重要作用。通过合理配置和使用,它将大大提升你的工作效率及项目质量。
30 4
|
3月前
|
JavaScript 前端开发 测试技术
TypeScript逆袭!大型项目为何对它情有独钟?揭秘背后的真相!
【8月更文挑战第27天】随着前端领域的快速发展,JavaScript已成为Web开发的核心语言。然而,在处理大型项目时,其弱类型特性导致的维护困难和易错性等问题日益突出。为解决这些问题,TypeScript应运而生,逐渐成为大型项目的首选方案。
36 3
|
3月前
|
前端开发 JavaScript 开发者
翻天覆地!ES6+新特性大爆发,揭秘JavaScript代码的惊人蜕变!
【8月更文挑战第27天】自ES6标准发布以来,JavaScript新增的特性极大地提升了编程效率并简化了代码。本文重点介绍了五大特性:函数默认参数简化、模板字符串的强大功能、解构赋值的便捷性、箭头函数的简洁之美。这些特性不仅使代码更加简洁、易读,还解决了传统写法中的诸多问题。通过学习和应用这些新特性,开发者可以编写出更高效、更优雅的代码,以应对日益复杂的编程挑战。
64 2
|
3月前
|
JavaScript 前端开发 开发者
【颠覆你的前端世界!】VSCode + ESLint + Prettier:一键拯救Vue代码于水深火热之中,打造极致编程体验之旅!
【8月更文挑战第9天】随着前端技术的发展,保持代码规范一致至关重要。本文介绍如何在VSCode中利用ESLint和Prettier检查并格式化Vue.js代码。ESLint检测代码错误,Prettier保证风格统一。首先需安装VSCode插件及Node.js包,然后配置ESLint和Prettier选项。在VSCode设置中启用保存时自动修复与格式化功能。配置完成后,VSCode将自动应用规则,提升编码效率和代码质量。
471 1
|
6月前
|
JavaScript 前端开发 API
分享一次使用某个JavaScript游戏框架开发项目的经历,遇到了哪些挑战以及如何解决的。
在Phaser框架下开发2D平台跳跃游戏"跳跃之旅"时,面临性能优化、碰撞检测与响应及图形动画等挑战。通过使用Phaser的性能分析工具、资源优化和内置物理引擎实现性能提升与精确碰撞。借助图形绘制API和动画系统,创造出精美流畅的游戏体验。此次项目提升了开发者的技术水平和对游戏开发的理解。
37 2
|
6月前
|
JavaScript 前端开发 中间件
ViteConf 2022回顾:Vite是如何诞生的?
ViteConf 2022回顾:Vite是如何诞生的?
|
移动开发 JavaScript 前端开发