Islands Architecture:「次世代」Web框架背后共同的架构选型,0KB JavaScript 的未来?
历史轮回
早在2010年,Facebook的科学家 Changhao Jiang 就提出了 Big Pipe 架构,用于解决后台生成 Web 页面导致的页面加载缓慢问题。
BigPipe 首先把HTML页面分为很多部分,然后在服务器和浏览器之间建立一条管道,HTML的不同部分可以源源不断地从服务器传输到浏览器。BigPipe 首先输送的内容是框架性HTML结构(aka 骨架屏),这个框架结构会定义每个Pagelet模块的位置和宽高。
随着前端技术的快速发展,后台生成HTML的Web开发渐渐被新时代的前端开发框架(Jquery/React/Vue)所取代,BigPipe 慢慢被众人遗忘。
2019年的某次会议上(已不可考证),Etsy的前端架构师 Katie Sylor-Miller 在某次会议上描绘了现代Web开发的组件岛屿模式("Component Islands" pattern)。这是「岛屿架构」这一名词在公共场合的首次出现。
2020年8月,preact作者 Jason Miller 在他的一篇博文中介绍了此种架构模式,并且命名为 Islands Architecture。
自此,Islands Architecture 被海外前端圈子所熟知。在后React时代,受此设计模式的启发,一个又一个新的Web开发框架如雨后春笋涌现出来。
一个很有意思的点,提出 Islands Architecture 的两位都就职于 电子商务 相关的公司。这不由得让人怀疑,Islands Architecture 与电子商务网站有着极大的「相性」。Islands Architecture的概念与 Big Pipe 相当相似:将页面拆解成为不同的小型 App,每个 App 都可以在服务端进行渲染HTML。当用户访问页面时,会先返回骨架屏。随后,通过不断返回不同的小型 App,并且在客户端进行 按需的、渐进式 Hydration( Progressive Hydration ),以此渲染完成整个页面。注意这两个词代表着不同的意义:
- 按需的:有一些小型App不需要被Hydrate
- 渐进式:Hydrate 分优先级
Islands Architecture 流派解析
上面的我们已经知道了 Islands Architecture 的目标与原理。接下来的问题变得相当简单:如何将 Islands Architecture 与现有技术体系(React / Vue / Angular, MVVM )相结合,从而实现 Islands Architecture 的应用?
官方正统: React
React 官方一直在探索如何优化 React 的性能与体验。2017年,前 mozilla 工程师 Lin Clark 在 React Conf 2017 上做了关于 Fiber 的介绍(A Cartoon Intro to Fiber),用漫画的方式形象地向我们展现了 Stack Reconciler 与 Fiber Reconciler 运行区别,Fiber Reconciler 就是 Concurrent 的雏形。
React 开发者应该对2018 JSConf Iceland 上 Dan Abramov 的 Beyond React 16 记忆犹新。Dan 演示的 Demo 一度引起了社区广泛的关注。我们可以明显感受到三种模式带来的体验差异,Sync 模式下页面是完全卡顿的,input 连续输入得不到响应,Debounced 模式下尽管连续输入流畅,但由于变更被统一延迟,下方图表没有随输入改变而重渲染,只有 Concurrent 下是正常的体验,输入流畅,图表也随之而变更。但是除去此种场景,Concurrent Mode 在未来将会为 React 带来更多想象空间。
在 2019 年 5 月的 Google I/O 开发者大会上,Chrome 团队的 Jason Miller (没错,还是他)和同事一起探讨了前端架构选与性能之间的关系:Rendering on the Web: Performance Implications of Application Architecture。在演讲中,Jason Miller 提出了 Progressive Hydration (上文已有介绍),并借助 React 框架快速实现了简单demo。
随后,2019年,Dan 在 twitter 上表示,React 将会支持 Progressive Hydration,不过必须借助 Concurrent Mode 的能力,让 Progressive Hydration 的达到开箱即用的目的。
与此同时,preact实现了 Progressive Hydration Production Ready 通用库:
三年过去了,在 React 这一边,Progressive Hydration 似乎被人遗忘,并没有任何的进展。直到 2022年5月,React Team 终于发布了React 18,带来了令人兴奋的 Concurrent Mode,除此之外,借助 Concurrent Mode 实现的 Progressive Hydration 正式发布(Flag回收)。与 preact 的声明式 Progressive Hydration 不同,react 18 的 Progressive Hydration 并不需要开发者做任何配置,一切都在 React 框架内悄无声息的进行。
借助 Concurrent Mode ,Hydration 将会通过识别用户的操作从而自动判断优先级并且执行。此外,React Server Component的出现使得 按需 Hydrate 和 Streaming SSR 变为可能;
至此,React 已经达成了 Islands Architecture 所有条件。等到 React Server Component RC, Islands Architecture 将会在 React 项目中成为优化 Web 性能的必要手段之一。
框架的框架:Astro
Astro是一个 可以自由地使用任何流行的组件库(React、Preact、Vue、Svelte、Solid 等)或 Astro 的类似 HTML 的组件语法来构建 UI 的静态网站生成器。它将高性能作为主要卖点,而其实现高性能的秘密,就是 Partial Hydration:
异步的极致:Qwik
React 的 Progressive Hydration 由框架决定在页面初始化时,按优先级进行JS Load 和 Hydrate。而 Qwik 则更为极端,JS Load / Hydration 发生在用户点击之后。详细的机制在这里不做多的解释,可以参考:如何移除你项目中99%的JS代码 - 掘金
除此之外,还有一些「次世代」Web框架也同样使用了 Islands Architecture 来优化 App 性能。
Islands Architecture + Deno:Fresh
Vue Like:MarkoMarko 是 ebay 前端开源的 Vue Like 框架,将 Progressive Rendering 作为主要特性之一。
服务化探索: Component as a Service
可以看到,业内的 Islands Architecture 都是在同一个项目内进行功能和组件的拆分。在此基础上,我们能否更进一步,将组件服务化,让每个组件都拥有自己专属的 Server Runtime。从而方便组件在不同的项目内进行复用?笔者之前做过一些探索:
但服务化后,给子App隔离、开发、调试、提出了更高的要求:
- 服务化后,将形成复杂的服务调用链路(例如循环嵌套链路),如何监控服务调用链路;
- 服务出错时,如何降级到CSR?
- 如何进行多个服务的协同开发/调试;
- 服务化后,由于服务的强隔离性,需要引入类似于 微前端 的服务隔离机制(CSS Scope / JS Sandbox),确保子应用在集成的时候不会冲突;
Islands Architecture 是未来吗
任何技术都有其适用的场景。如果网站是纯静态的(新闻/博客),使用静态网站生成器(SSG),可以带来更好的性能表现。但如果网站功能繁杂,需要复杂的交互、繁多的组件(例如 电商 / 在线文档),那 Islands Architecture 可以大大缓解 SSR 所带来的缺陷:
- 按需 Hydration -> 不错的TTFB
- 渐进式 Hydration -> TTI close to FCP
- Streaming Pipe -> 没有 buffered
不过需要注意的是,Islands Architecture 引入了一系列的代价,这是无法避免的: - 应用必须已经完全支持同构SSR - 改造成本
- 代码必须拆成没有(或极少)互相依赖的组件(或chunk)- 组件依赖
- 需要开发者定义 Hydrate 顺序(如果使用了React,框架已经处理)
- 确保 Hydrate 不阻塞用户操作(如果使用了React,框架已经处理)
- 需要定义组件加载态UI(或许可以考虑骨架屏自动生成)
参考资料
推荐阅读
https://www.patterns.dev/