Astro 群岛(Astro Islands)
Astro 群岛指的是静态 HTML 中的交互性的 UI 组件。一个页面上可以有多个岛屿,并且每个岛屿都被独立呈现。你可以将它们想象成在一片由静态(不可交互)的 HTML 页面中的动态岛屿。
Astro 对于群岛的说明,可以看出主要就是可以动态交互的 UI 组件就称为群岛!
为什么要群岛
群岛是为了解决什么问题呢?目前大部分的 SSR、SSG 应用都是服务端返回整个页面的 HTML ,然后注入一段 JS 脚本进行事件绑定数据初始化等操作才能进入交互(也就是经常说的注水 Hydration1)。那这种方式有什么问题呢?
对于比较简单的也没还是比较能接受的,当页面的内容越来越多交互越来越多,那么返回的js会越来越大要执行的脚本也越来越复杂。这导致了一个问题页面的可交互的时间( TTI )会越来越长。
Astro 群岛怎么解决的这个问题
通常我们的页面不会全都是需要交互的内容,那么对于不需要交互的部分我们就不需要参与到 Hydration 到过程!仅仅对需要交互的部分进行 Hydation 。Astro 就是通过这种局部(partial)或说是选择性的注水(selective hydration)来缩短注水的时间!
Astro 默认生成不含 JavaScript 脚本的页面!当我们使用 Vue、React、Svelte 等框架的时候,Astro 会将其渲染为 HTML ,其脚本部分则会根据设置在不同的时机在进行注水!
具体实现
了解 Astro 中这部具体的实现方式,不可避免的需要先了解它的基本语法是如何解析的,然后遇到需要有交互部分又是如何解析的,最后到浏览器执行和进行注水又是如何进行的!
Astro 模版解析
Astro 文件将组件中的脚本代部分放在栅栏(---)里面, 栅栏下面写于 JSX 类似的模板语法!这部分是如何进行解析的呢?
Astro 本身是基于 Vite 进行打包构建的,Astro 文件的解析是 vite-plugin-astro
插件来完成的!在插件内部通过 transform 钩子处理编译代码。Astro 的 编译器 是通过 Go 编写打包成 wasm 执行,它主要功能是将 Astro 解析为合法的 ts 并生成 sourcemap!
比如 example.astro
这个 astro 组件
通过 Astro 编译器将,再通过 Vite 的 transformWithEsbuild
也就是 esbuild 编译符合 esm 的内容。最终生成内容:
最后通过 server runtime 的 renderPage
生成一个流,
最终转换成一个完整的 HTML 页面。
island 组件如何解析
在 Astro 可以通过客户端指令(cliend:*
)将组件将组件变成一个 island 组件。
下面中引用了一个 Vue 的组件,通过 client:visible
将告诉 Astro 该组件将会在页面中可见的时候进行 Hydration
上面编译后将生成的内容
其中的 renderComponent
方法, Astro 会判断需要渲染的 Component 是否是一个框架的组件是否需要进行 Hydration,对于需要的,会先找到该框架对应 ssr 渲染器渲染对应的 HTML,这时还会给客户端响应页面HTML时注入一段辅助Javascript片段,这会根据使用的指令不同注入不同的片段。主要是在定义 astro-island
web component 实现指定时机进行以及加载处理 Hydration 相关逻辑 (/workspaces/astro/packages/astro/src/runtime/server/astro-island.ts
)。最后生成 `<astro-island … />。 最终生成的内容就会是:
浏览器如何加载
根据我们在组件上加的 cliend:*
不同属性表示该组件注水的时机,目前 Astro 提供的客户端指令以及代表的时间节点如下:
-
client:load
表示在页面加载阶段
-
client:idle
表示页面完成了初始加载,并触发 requestIdleCallback(如不支持使用的 setTimeout 200ms)事件
-
client:visible
表示组件进入用户的视口
-
client:media
满足指定的媒体查询 matchMedia api
-
client:only
跳过 HTML 服务端渲染,只在客户端进行渲染
比如前面 Counter
使用的是 client:visible
指令,当该组件进入视口就会去加载该组件需要的脚本然后进行 Hydration。