Astro
框架定位
接下来聊一聊 Astro 框架的定位,是像 Vue、React 这样的底层渲染框架,还是像 Next.js 这种上层的研发框架?
这一点其实挺困扰初学者的,因为 Astro 既自创了类似于 .vue、.jsx 文件的 .astro 语法,又提供了像 Next.js 里面各种运行时的能力,比如约定式路由、构建优化、SSR 等等。
但实际上它给自己的定位非常清晰,即 content-focused 应用开发框架,换句话说,就是重内容、轻交互场景下的上层研发框架,比如大多数电商网站、文档站、博客站、证券网站等等。
你可以将 Astro 理解为一个垂直场景下的 Next.js,但它可以在它适用的领域里面可以胜过其它所有竞品 (如 Next.js、Remix、Vuepress 等),这是它能够做起来的重要原因。接下来,我们就来看看 Astro 的优势在于哪些地方。
核心优势
Astro 的主要优势包括如下几点:
- Islands 架构,解决传统 SSR/SSG 框架的全量 hydration 问题,做到尽可能少的 Client 端 JS 的开销,甚至是 0 JS。
- 学习成本低。
.astro语法和传统的.jsx和.vue非常相似,对于新手前端来说也比较容易掌握。 - 使用灵活。对于页面的开发,你既可以使用官方的
.astro语法,也同样可以使用.md、.vue、.jsx语法,也就是说,你可以自由选择其它前端框架的语法来开发,甚至可以在一个项目中同时写 Vue 组件和 React 组件! - 构建迅速。底层构建体系基于 Vite 以及 Esbuild 实现,项目启动速度非常快。
Islands 架构
在如上的几个优点中,我们来重点说一说 Astro 的 Islands 架构,因为这是它高性能最主要的原因。
Islands 架构模型早在 2019 年就被提出来了,并在 2021 年被 Preact 作者 Json Miller 在 Islnads Architecture 一文中得到推广。这个模型主要用于 SSR (也包括 SSG) 应用,我们知道,在传统的 SSR 应用中,服务端会给浏览器响应完整的 HTML 内容,并在 HTML 中注入一段完整的 JS 脚本用于完成事件的绑定,也就是完成 hydration (注水) 的过程。当注水的过程完成之后,页面也才能真正地能够进行交互。
那么当应用的体积逐渐增大时,需要在客户端执行的 JS 脚本也会越来越多,这也意味着 TTI(可交互时间) 指标越来越高:
为了解决这个问题,Islands 架构将页面拆分为各自独立的组件,包含 静态组件 和 可交互组件,如下图的例子所示:

可以清楚的看到,一个页面中只有部分的组件交互,那么对于这些可交互的组件,我们可以并行地执行 hydration 过程,因为组件之间是互相独立的。
而对于静态组件,即不可交互的组件,我们可以让其不参与 hydration 过程,直接复用服务端下发的 HTML 内容。
可交互的组件就犹如整个页面中的孤岛 (Island),因此这种模式叫做 Islands 架构:
相比于传统 SSR 中的全量 hydration,Islands 模式可以实现局部 (partial) hydration,从而优化 JS 的体积,减少网络传输的成本和 JS 运行时的开销。
在 Astro 中,默认所有的组件都是静态组件,比如:
// index.astro
import MyReactComponent from '../components/MyReactComponent.jsx';
---
<MyReactComponent />值得注意的是,这种写法不会在浏览器添加任何的 JS 代码。但有时我们需要在组件中绑定一些交互事件,那么这时就需要 激活孤岛组件 了,在 Astro 如何来激活呢?其实很简单,在使用组件时加上 client:load 指令即可:
// index.astro
---
import MyReactComponent from '../components/MyReactComponent.jsx';
---
<MyReactComponent client:load />如此一来,Astro 会给浏览器传输一部分 JS 代码供这个组件完成 hydration,以便后续的交互。
Astro 2.0
- 内容集合:Markdown 和 MDX 的自动类型安全;
- 混合渲染:支持静态渲染和动态渲染;
- 重新设计的错误 Overlays;
- 改进的开发服务器 (HMR);
- Vite 4.0;
- 新的公开路线图
详情参考:Astro 2.0正式发布,现代化静态站点生成器 - 掘金
SSR 和 SSG
1. 静态资源和动态资源分别是什么?
首先静态资源和动态资源都是服务端这边的概念,因为我们访问互联网本质都是访问对应的服务端
对于服务端来说:
- 静态资源是:提前准备好的,写死了的,直接文件 IO 就可以 response 的属于静态资源。
- 动态资源是:不是写死的,需要读库的 或 需要调下游接口的 或 需要脚本处理的属于动态资源
前端角度看哪些是静态资源?
- 通过前端工程 npm run build 编译好的 js、css、html 文件,都属于静态资源
- 提前准备好的文件(比如自己开发的源代码),都属于静态资源
- 图片、视频等资源文件,都属于静态资源
前端角度看哪些是动态资源?
- 需要调接口才能得到的内容,并且内容不是提前准备好的,属于动态资源
2. 从架构角度看动、静态资源
1. 请求SSR 架构的服务的 html 属于静态资源还是动态资源?
结论:属于动态资源
因为:html 没有提前准备好,没有提前静态化。是来一个请求,就动态编译生成 html 的
2. 请求SSG 架构的服务的 html 属于静态资源还是动态资源?
结论:属于静态资源
因为:html 被提前静态化了。无需实时编译
3. 引发对性能优化的思考
SSR 架构的存在问题,以及如何解决
SSR 架构性能好是最大的特色之一,但被人诟病的一个最大问题也是性能问题,原因:
SSR 架构的性能好,其实是针对前端来说的
对于前端用户来说,访问 SSR 的服务,可以直接得到完整的 html(CSR 架构的 html 是空的,没有 dom 内容,dom 内容需等 js 后续生成的),
并且如果你的首屏需要被多个接口阻塞时,SSR 可以在服务端把请求处理完
服务端处理请求非常非常快,举个栗子:同一个接口前端 ajax 需要 1s,服务端请求可能只需 20ms,因为服务端可以抹掉网络连接的阻塞,可能和目标下游服务器就在同一个机房
SSR 架构对于服务端来说,性能非常差
- 这个怎么理解呢? 其实是和服务端接口来做对比的,比如接口的 QPS 可以很容易超过 1000,但 SSR 的处理 QPS 可能只有 10,因为 html 是动态生成的,需要大量的时间来编译得到 html,所以对于接口来说,SSR 的性能很差很差
解决办法
做成 SSG(静态化)
原理:提前编译好 html,节省编译 html 的时间,让 请求动态资源 变成 请求静态资源。
但也不是没副作用的,副作用是:会丢失动态化能力,比如我本来可以在服务端根据用户的 ip,显示对应的语言的 html。做成 SSG 之后,只能默认显示一种语言。并且也无法在服务端把阻塞请求处理完
不过有办法可以解决上面 SSG 架构的缺陷(动静结合!)
原理:在多加一层 bff 层,由这一层来处理动态化部分
比如 把阻塞请求处理完,通过
占位标识,替换掉对应 html 内的数据。比如 根据用户 ip 显示多语言的问题,需要我们提前用 ssg 编译好多份 html(对应多语言),然后由 bff 来处理。。(确实做的有点复杂了,不过假如要追求极致性能的话,这是一种选择)
参考
对静态资源和动态资源的思考,延伸至SSR和SSG的性能优化 - 掘金