解剖Babel —— 向前端架构师迈出一小步

发表于 3年以前  | 总阅读数:547 次

当聊到Babel的作用,很多人第一反应是:用来实现API polyfill

事实上,Babel作为前端工程化的基石,作用远不止这些。

作为一个庞大的家族,Babel生态中有很多概念,比如:presetpluginruntime等。

这些概念使初学者对Babel望而生畏,对其理解也止步于webpackbabel-loader配置。

本文会从Babel的核心功能出发,一步步揭开Babel大家族的神秘面纱,向前端架构师迈出一小步。

Babel是什么

Babel 是一个 JavaScript 编译器。

作为JS编译器,Babel接收输入的JS代码,经过内部处理流程,最终输出修改后的JS代码。

Babel内部,会执行如下步骤:

  1. Input Code解析为AST(抽象语法树),这一步称为parsing
  2. 编辑AST,这一步称为transforming
  3. 将编辑后的AST输出为Output Code,这一步称为printing

从Babel仓库[1]的源代码,可以发现:Babel是一个由几十个项目组成的Monorepo

其中babel-core提供了以上提到的三个步骤的能力。

babel-core内部,更细致的讲:

  • babel-parser实现第一步
  • babel-generator实现第三步

要了解第二步,我们需要简单了解下AST

AST的结构

进入AST explorer[2],选择@babel/parser作为解析器,在左侧输入:

const name = ['ka', 'song'];

可以解析出如下结构的AST,他是JSON格式的树状结构:

babel-core内部:

  • babel-traverse可以通过「深度优先」的方式遍历AST
  • 对于遍历到的每条路径,babel-types提供用于修改AST节点的节点类型数据

所以,整个Babel底层编译能力由如下部分构成:

当我们了解Babel的底层能力后,接下来看看基于这些能力,上层能实现什么功能?

Babel的上层能力

基于BabelJS代码的编译处理能力,Babel最常见的上层能力为:

  • polyfill
  • DSL转换(比如解析JSX
  • 语法转换(比如将高级语法解析为当前可用的实现)

由于篇幅有限,这里仅介绍polyfill「语法转换」相关功能。

polyfill

作为前端,最常见的Babel生态的库想必是@babel/polyfill@babel/preset-env

使用@babel/polyfill@babel/preset-env可以实现高级语法的降级实现以及APIpolyfill

从上文我们知道,Babel本身只是JS的编译器,以上两者的转换功能是谁实现的呢?

答案是:core-js

core-js简介

core-js是一套模块化的JS标准库,包括:

  • 一直到ES2021polyfill
  • promisesymbolsiterators等一些特性的实现
  • ES提案中的特性实现
  • 跨平台的WHATWG / W3C特性,比如URL

core-js作者Denis Pushkarev从core-js仓库[3]看到,core-js也是由多个库组成的Monorepo,包括:

  • core-js-builder
  • core-js-bundle
  • core-js-compat
  • core-js-pure
  • core-js

我们介绍其中几个库:

core-js

core-js提供了polyfill的核心实现。

import 'core-js/features/array/from'; 
import 'core-js/features/array/flat'; 
import 'core-js/features/set';        
import 'core-js/features/promise';    

Array.from(new Set([1, 2, 3, 2, 1]));          // => [1, 2, 3]
[1, [2, 3], [4, [5]]].flat(2);                 // => [1, 2, 3, 4, 5]
Promise.resolve(32).then(x => console.log(x)); // => 32

直接使用core-js会污染全局命名空间和对象原型。

比如上例中修改了Array的原型以支持数组实例的flat方法。

core-js-pure

core-js-pure提供了独立的命名空间:

import from from 'core-js-pure/features/array/from';
import flat from 'core-js-pure/features/array/flat';
import Set from 'core-js-pure/features/set';
import Promise from 'core-js-pure/features/promise';

from(new Set([1, 2, 3, 2, 1]));                // => [1, 2, 3]
flat([1, [2, 3], [4, [5]]], 2);                // => [1, 2, 3, 4, 5]
Promise.resolve(32).then(x => console.log(x)); // => 32

这样使用不会污染全局命名空间与对象原型。

core-js-compat

core-js-compat根据Browserslist维护了不同宿主环境、不同版本下对应需要支持特性的集合。

Browserslist[4]提供了不同浏览器、node版本下ES特性的支持情况

Browserslist

比如:

"browserslist": [
    "not IE 11",
    "maintained node versions"
  ]

代表:非IE11的版本以及所有Node.js基金会维护的版本。

@babel/polyfill与core-js关系

@babel/polyfill可以看作是:core-jsregenerator-runtime

regenerator-runtimegenerator以及async/await的运行时依赖

单独使用@babel/polyfill会将core-js全量导入,造成项目打包体积过大。

从Babel v7.4.0[5]开始,@babel/polyfill被废弃了,可以直接引用core-jsregenerator-runtime替代

为了解决全量引入core-js造成打包体积过大的问题,我们需要配合使用@babel/preset-env

preset的含义

在介绍@babel/preset-env前,我们先来了解preset的意义。

初始情况下,Babel没有任何额外能力,其工作流程可以描述为:

const babel = code => code;

其通过plugin对外提供介入babel-core的能力,类似webpackplugin对外提供介入webpack编译流程的能力。

plugin分为几类:

  • @babel/plugin-syntax-*语法相关插件,用于新的语法支持。比如babel-plugin-syntax-decorators[6]提供decorators的语法支持
  • @babel/plugin-proposal-*用于ES提案的特性支持,比如babel-plugin-proposal-optional-chaining可选链操作符特性支持
  • @babel/plugin-transform-*用于转换代码,transform插件内部会使用对应syntax插件

多个plugin组合在一起形成的集合,被称为preset

@babel/preset-env

使用@babel/preset-env,可以「按需」core-js中的特性打包,这样可以显著减少最终打包的体积。

这里的「按需」,分为两个粒度:

  • 宿主环境的粒度。根据不同宿主环境将该环境下所需的所有特性打包
  • 按使用情况的粒度。仅仅将使用了的特性打包

我们来依次看下。

宿主环境的粒度

当我们按如下参数在项目目录下配置browserslist文件(或在@babel/preset-envtargets属性内设置,或在package.jsonbrowserslist属性中设置):

not IE 11
maintained node versions

会将「非IE11」「所有Node.js基金会维护的node版本」下需要的特性打入最终的包。

显然这是利用了刚才介绍的core-js这个Monorepo下的core-js-compat的能力。

按使用情况的粒度

更理想的情况是只打包我们使用过的特性。

这时候可以设置@babel/preset-envuseBuiltIns属性为usage

比如:

a.js

var a = new Promise();

b.js

var b = new Map();

当宿主环境不支持promiseMap时,输出的文件为:

a.js

import "core-js/modules/es.promise";
var a = new Promise();

b.js

import "core-js/modules/es.map";
var b = new Map();

当宿主环境支持这两个特性时,输出的文件为:

a.js

var a = new Promise();

b.js

var b = new Map();

进一步优化打包体积

打开babel playground[7],输入:

class App {}

会发现编译出的结果为:

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var App = function App() {
  "use strict";

  _classCallCheck(this, App);
};

其中_classCallCheck为辅助方法。

如果多个文件都使用了class特性,那么每个文件打包对应的module中都将包含_classCallCheck

为了减少打包体积,更好的方式是:需要使用「辅助方法」module都从同一个地方引用,而不是自己维护一份。

@babel/runtime包含了Babel所有「辅助方法」以及regenerator-runtime

单纯引入@babel/runtime还不行,因为Babel不知道何时引用@babel/runtime中的「辅助方法」

所以,还需要引入@babel/plugin-transform-runtime

这个插件会在编译时将所有使用「辅助方法」的地方从「自己维护一份」改为从@babel/runtime中引入。

所以我们需要将@babel/plugin-transform-runtime置为devDependence,因为他在编译时使用。

@babel/runtime置为dependence,因为他在运行时使用。

总结

本文从底层向上介绍了前端日常业务开发会接触的Babel大家族成员。他们包括:

底层

@babel/core(由@babel/parser@babel/traverse@babel/types@babel/generator等组成)

他们提供了Babel编译JS的能力。

注:这里@babel/core为库名,前文中babel-core为其在仓库中对应文件名

中层

@babel/plugin-*

Babel对外暴露的API,使开发者可以介入其编译JS的能力

上层

@babel/preset-*

日常开发会使用的插件集合。

对于立志成为前端架构师的同学,Babel是前端工程化的基石,学懂、会用他是很有必要的。

能看到这里真不容易,给自己鼓鼓掌吧。

希斯特利亚笔芯

参考资料

[1]Babel仓库: https://github.com/babel/babel/tree/main/packages

[2]AST explorer: https://astexplorer.net/

[3]core-js仓库: https://github.com/zloirock/core-js/tree/master/packages

[4]Browserslist: https://github.com/browserslist/browserslist

[5]Babel v7.4.0: https://babeljs.io/docs/en/babel-polyfill#docsNav

[6]babel-plugin-syntax-decorators: https://github.com/babel/babel/tree/main/packages/babel-plugin-syntax-decorators

[7]babel playground: https://babeljs.io/repl#?browsers=&build=&builtIns=false&spec=false&loose=false&code_lz=MYGwhgzhAECCAO9oG8C-Q&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=script&lineWrap=true&presets=env&prettier=false&targets=&version=7.13.7&externalPlugins=babel-plugin-transform-regenerator%406.26.0

本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/UNaah5CzAYSTROmkwe9w4g

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237231次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8065次阅读
 目录