ESModule 作为 JS 的标准模块机制,在日常开发中被广泛使用,但在大部分情况下,我们可能只是将其作为 JS 代码文件的组织形式来对待。作为 JS 的模块规范,ESModule 底层其实有一套非常完善的机制,来确保 ESModule 在不同场景下的性能以及行为的确定性。本文的主要内容是关于 ESModule 加载运行的相关原理和机制的分享,在理解了相关的原理和机制之后,你将会对平常在使用 ESModule 过程中遇到的一些问题(比如:循环引用在什么情况下会报错、TreeShaking 的原理等)有更加深入的理解。
下面用一个包含循环引用的例子来分享 ESModule 的加载和执行过程-
// main.mjs
import { mod1Fn } from './mod1.mjs'
import { mod2Fn } from './mod2.mjs'
mod1Fn('main')
mod2Fn('main')
// mod1.mjs
import './mod2.mjs'
export let mod1Value = 'mod1Value'
export function mod1Fn(from) {
console.log(`${from} call mod1Fn\n`)
}
// mod2.mjs
import { mod1Fn, mod1Value } from './mod1.mjs'
export function mod2Fn(from) {
console.log(`${from} call mod1Fn\n`)
console.log('log mod1Value in mod2Fn')
console.log(mod1Value)
}
mod1Fn('mod2')
mod2Fn('mod2')
以上的代码内容分别描述了 main.mjs、mod1.mjs、mod2.mjs 3 个文件的内容,下面我们通过 node 来运行以上的代码,执行 node index.mjs ,输出结果如下
可以看出循环引用在 ESModule 中是可用的,但如果我们在 mod2.mjs 中增加一行调用 mod2Fn 的代码,如下所示:
然后再执行代码,会发现实际执行会报错:
以上的报错是否似曾相识,从报错的信息我们大致可以推断出这个是一个和变量提升有关的报错,实际上报错的根因是在执行 mod2Fn('mod2') 时,mod1Value 还没有完成初始化,类似是下面这样的情况:
console.log(mod1Value)
let mod1Value = 'mod1Value'
但是从直观上看,mod2.mjs 是在 mod1.mjs 之后加载的,为什么 mod1Value 会没有初始化呢,会不会是因为 import 的位置在 mod1Value 之前的原因,导致没有完成初始化呢,但实际上,即使将 mod2 的 import 后置,比如以下的代码
仍然还是会报错,因此我们可以得出报错的原因和 import 的位置是无关的。实际上 ESModule 的加载和执行过程并不是简单的一个按顺序执行的流程,下面我们从底层原理的角度,分享一下 ESModule 实际是如何被加载和执行的,以及会出现以上报错的原因。
ESModule 的加载和解析过程整体上可以拆分为三个步骤:
下面我们还是以上面的代码为例,分享实际的过程
首先浏览器或者 Node 等应用程序会通过网络请求或文件读写等形式获取到对应的 ESModule 的代码,比如上文中的代码 node main.mjs Node 会逐步执行以下操作:
在完成这一步之后,我们会得到下面这样一张图
值得关注的是,以上的这些过程 JS 代码还没有被执行,是通过解析代码文本的方式,完成了模块的依赖解析和加载,以及建立模块之间的依赖关系。
上面一步完成了模块之间的依赖关系生成,接下来实例化本质上是更进一步,完成每个模块内部的变量的声明以及构建模块间的引用关系。在这个过程中有几个要注意的点:
还是以上面的例子为例,完成实例化之后我们会得到以下的依赖关系,具体的结果可见下图:
绑定的过程会有两种形式:
同样在这一个步骤,JS 代码仍然没有进入执行阶段
接下来进入实际的执行代码阶段,也是 JS 引擎开始执行代码的时机。整体的执行策略会遵循两个大的规则:
由第1阶段的依赖关系图,我们可以看出,依赖最深的是 mod2.mjs,所有 JS 引擎会先执行 mod2.mjs 中的代码,即:
然后根据第 2 阶段实例化代码得的到绑定关系图,会先执行以下红框中的部分
从上往下依次执行 mod2 中的代码,在执行到 12 行 mod2Fn('mod2') 时,mod2Fn 在第 7 行依赖了 mod1Value,而由上图我们可以看到,mod1Value 的状态是还未初始化,因此在执行 console.log(mod1Value) 时,代码会抛出没有初始化的错误。如果将 mod1.mjs 中的 let mod1Value 改为 var mod1Value,由于 var 天然有变量提升的特性,会先初始化为 undefined,实际运行时不会报错,会输出 undefined。
我们可以看出 ESModule import 不是简单的类似 require 的同步加载机制,下面我们来分析一下相比于同步的加载方式,ESModule 的加载策略上有哪些优势。
在 Web 领域,网络的加载耗时一直是用户体验非常重要的影响因子,在 ESModule 的策略下,实际模块的依赖的解析不需要依赖代码的执行,而是直接通过静态分析的方式进行,这使得浏览器、Node 等应用可以用尽可能快的速度完成依赖的收集和资源的请求,而不会受具体模块代码执行耗时以及前后顺序的影响,可以使用尽可能多的并发请求来快速完成加载。
同时从最开始的例子中可以看出,在 ESModule 中 import 在文件的中的位置不会影响具体的行为表现,这使得浏览器可以进行类似 HTML 流式渲染一样,对 ESModule 进行 “流式加载”,比如一个 JS 文件有 1000 行,如果第一行写了一个 import,浏览器就可以直接进行对应模块的加载,而无需等待文件加载完成在进行下一个模块的加载。
ESModule 在实例化的阶段会完成相关变量的声明和绑定,在这个阶段我们可以得到对应的绑定关系图,比如之前例子中的以下这张图
通过这张图我们可以明确的看出 mod2Value 没有被其他模块所引用,从而我们只需要判断在 mod2 内也没有使用 mod2Value ,则 mod2Value 相关的代码是无用的,这也是 TreeShaking 的原理。而在这一阶段,实际的代码还没有被执行,以上的依赖关系完全是按照代码文本的静态分析得出,所以这也保证了,我们在构建时也可以模拟浏览器或Node 进行类似的操作,生成对应的依赖关系图,然后针对单个模块分析哪些方法或变量时没有用,对代码进行自动的删减。
本文简要的介绍了 ESModule 加载和执行的整体过程,在研究的过程也深刻感受到了 ESModule 整体规范的严谨性和完善性,考虑了诸多不同的方面,并不是简单的 CommonJS 的升级版本。对于底层原理更加深入的理解,也能够指导我们在使用一些新的技术能够更有方向性,比如 TreeShaking ,实际上就是充分利用了 ESModule 的规范,实现了非常优雅的代码自动剔除,能够保证几乎 0 成本的代码体积最优。实际上 ESModule 还有很多其他内容,比如 dynamic import、top level await 等,可以值得更多的研究和探索。
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/wxUz5E1Xs5dqYFPRPOnAlw
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。