发布于 2026-01-06 10 阅读
0

前端框架的基本结构

前端框架的基本结构

本系列文章旨在探索前端框架的内部机制,揭示这些框架底层的运行原理。对于初学者或不常需要了解代码底层运行情况的人来说,本系列文章可能不太合适。

动机

这取决于你来自哪个国家:

  • Vue 3:你有没有想过,为什么watchEffectVue 能在响应式状态改变时自动重新运行,而无需显式指定?在 React 中,你必须显式地指定useEffect依赖项。
  • React:你有没有想过为什么在使用 Hooks 时会出现状态/闭包失效的情况?为什么 Hooks 的顺序很重要,而且你必须始终在 React 函数的最顶层使用 Hooks?
  • Solid:为什么这个类似 React 的组件函数只运行一次,却依然能实现相同(甚至更好)的功能?为什么信号获取器是一个函数?而且它为什么这么快
  • ETC。

如果你曾问过自己这些问题,并且想要深入挖掘以找到答案,那么你会发现这个系列很有趣。

本系列文章将逐步深入探讨这些库/框架的内部机制,直到我们能够理解并独立构建每个库/框架的简化版本。我们或许无法最终达成目标,但在这个过程中,我们一定会受益匪浅。

在本系列的第一部分中,让我们来了解一下前端框架的基本结构。

前端框架的基本结构

目前流行的前端框架可以分为两类:一类是带有虚拟 DOM 的,另一类是不带虚拟 DOM 的。

首先,我们来看使用虚拟 DOM 的前端库/框架(例如 Vue、React)。这些框架的基本结构可以描述如下:

带有虚拟 DOM 的前端框架的基本结构

从宏观层面来看,该组织结构中各单元的角色如下:

  1. 编译器:模板或 JSX 被输入到编译器中,然后编译器输出渲染函数代码。
  2. 虚拟 DOM 渲染器/协调器:该单元负责调用渲染函数并管理虚拟 DOM 树(例如,响应状态变化并相应地更新虚拟 DOM)。React 将此组件称为协调器(源代码在),而 Vue 则称之为运行时渲染器。之所以称之为协调器,是因为它的主要任务之一是执行虚拟 DOM 的“差异比较”或“协调”。
  3. 响应式(状态):此单元处理每个组件以及整个应用程序中的响应式和状态。它通常与此结构中的其他所有单元解耦,因此您可以创建可重用的“钩子”,而无需任何渲染函数。
  4. 原生渲染器:该单元从虚拟 DOM 渲染器/协调器“获取渲染指令”,然后根据目标环境决定实际的渲染方式。例如,如果目标是 Web,它将渲染并更新实际的 DOM。当目标为 Web 时,Vue 使用@vue/runtime-dom,它通常被“抽象化”;而 React 使用react-dom,它没有被抽象化,需要显式导入和使用。两者都允许在目标为非 DOM 环境时创建自定义渲染器:

那么,那些不使用虚拟 DOM 的框架呢?嗯,虚拟 DOM 渲染器/协调器和虚拟 DOM 树已经不存在了:

不使用虚拟 DOM 的前端框架的基本结构

这里有几点值得注意:

  1. 不要被箭头的方向误导。看着上面的图,你可能会认为响应式单元需要直接调用渲染器,而渲染器需要了解响应式系统的方法。但响应式的工作原理并非如此。我们将在本系列的后续文章中继续探讨和理解这一点。

事实上,你可以在不更改渲染器的情况下,完全用另一个响应式系统替换当前的响应式系统,反之亦然。例如:

  1. 当目标环境发生变化时,模板/JSX单元、编译器和原生渲染器肯定会受到影响。例如,如果目标环境是Canvas或原生移动应用,则无法<div>Hello World</div>在模板/JSX中编写代码。
  2. 这仍然是一个简化的结构。例如,我们还没有考虑调度器,而调度器是这些前端框架中非常重要的组成部分。在本系列的后续文章中,当我们讨论调度器背后的动机时,我们会提供一个包含调度器的更新图表。

现在我们已经了解了前端框架的基本结构,接下来我们将逐一探讨每个库/框架结构中的各个单元。我计划以 Vue 3 为例来探讨这些单元,因为这是我目前在项目中广泛使用的框架(尽管我也大量使用过 React、Solid 和 Svelte,并且我计划将它们也纳入本系列文章中)。

结论

在本系列文章的这一部分,我们探讨了前端框架的基本结构。在下一部分中,我们将继续探索 Vue 3 的响应式设计,并使用简化的版本来ref演示watchEffect

希望这对您有所帮助。如果您有任何建议或意见,请随时在评论区留言。

文章来源:https://dev.to/itswillt/exploring-frontend-frameworks-internals-part-1-the-basic-struct-of-frontend-frameworks-vue-3s-reactivity-4neg