资讯动态

Astro vs Next.js:从零JS到群岛架构的性能实战评测

发布时间:2026/9/18 5:04:24 来源:尧图企业网站定制
别急着给 Next.js 判死刑先看看你手里拿的到底是什么“锤子”如果你是个天天跟 React、Vue 打交道的前端最近大概率被 Astro 刷屏了。铺天盖地的“放弃 Next.js拥抱 Astro”“首屏零 JS”“速度提升 100%”……看得人心痒痒又隐隐觉得哪里不对Astro 真的能替代 Next.js 吗如果它真的这么好为什么大厂生产线还没全面切换先说结论Astro 不是来取代 Next.js 的它更像个“定向爆破手”专治内容型网站的精神内耗。如果你做的就是博客、文档站、营销落地页、产品官网、电商的商品详情页那 Astro 的“群岛架构”确实是降维打击——能让你在几乎没有 JS 的情况下把首屏渲染速度拉到极致。但如果你想做的是复杂的 SaaS 后台、实时协作白板、重度数据可视化的控制台硬上 Astro 反而会被自己折腾疯。这篇文章我会拿一个真实的博客站点做对比实测同样的内容一套用 Next.js 构建一套用 Astro 构建两套都部署到相同的边缘网络然后我逐个对比首屏大小、请求数、LCP 指标、TBTTotal Blocking Time和构建体验把“群岛架构”这层窗户纸彻底捅破。文章里会穿插大量实操细节包括 Astro 的核心原理、组件写法、指令选择、部署注意事项以及我踩过的一些坑。内容比较多建议先收藏再慢慢看。1. 为什么“默认零 JS”这个思路这么香1.1 先看看 Next.js 里一个页面到底背了多少 JS我们天天在用 React 或者 Vue 这类框架可能已经忘记了最早那个“一个 HTML 文件走天下”的时代是什么体验了。React 组件要跑起来必须先下载 React Runtime再下载 React DOM再下载应用代码然后浏览器解析、编译、执行最后才能做首次渲染。注意这里说的是“客户端渲染”也就是 CSR页面初始 HTML 可能就是空壳所有内容靠 JS 拼接出来。Next.js 在一定程度上解决了这个问题它支持 SSR 和 SSG能在服务端生成 HTML。可问题来了React 的交互逻辑依然需要水合Hydration也就是说服务端渲染出来的 HTML 给了你但浏览器为了让你能点按钮、开菜单、触发事件还是得在页面上重新执行一遍组件代码把事件监听器“黏”到已经渲染好的 DOM 上。这个过程叫“注水”。它的代价是哪怕页面上只有一个汉堡菜单按钮需要交互你也得下载完整的 React 运行时和应用组件代码。代码量直接从几 KB 变成几百 KB这对网络环境不好的用户来说非常不友好。我实测过一个用 Next.js 做的企业官网首页静态内容其实是纯文本加几张图片但生产构建产物里 JS 文件大小加起来超过 400KB首屏还需要额外请求两三轮 JS 之后交互元素才完全可用。明明用户只是想读文字却被迫下载了一整个“应用程序”。1.2 Astro 的哲学无交互 零 JSAstro 的思路完全不同。它默认你做出来的页面是“静态的”也就是一个纯粹的 HTML CSS 组合。Astro 组件在构建时直接在服务端编译成字符串输出组件的 JS 不会被发送到浏览器端。只有在某个组件你明确标记为需要交互时Astro 才会把它单独作为一个小型“岛屿”让它的 JS 按需加载。这就是“群岛架构”的来源整张页面就是一整片“静态海洋”中间散布着若干可交互的“岛屿”。这些岛屿彼此独立互不干扰哪个需要交互就只加载哪一个的 JS而不是一荣俱荣、一损俱损地全量加载。打个生活化的比方Next.js 的做法是你要开一个线下的餐厅为了让每个包间都能随时叫服务员老板给整个大堂配了一套全覆盖的智能通话系统所有房间都必须通电、联网、装喇叭即使某个包间一整天都没人用系统也照常装着。Astro 的做法是你只在真正有人的包间门口挂个服务铃没人的包间一概不装自然省电省钱。1.3 “零 JS”到底是不是绝对的字面意思需要澄清一个概念Astro 说的“零 JS”指的是“静态部分不输出 JS”。如果你的页面完全没有交互组件那它确实能做到生产的 HTML 页面里一个 JS 文件都没有甚至连框架运行时都不存在。但只要页面里有一个评论框、一个导航折叠按钮或者一个主题切换器Astro 就会为这个“岛”单独打包一份 JS且这份 JS 只包含该组件及其依赖不会牵扯别的组件。这个设计让“首屏速度提升 100%”这句宣传语显得并不夸张。因为去掉 JS 之后浏览器的主线程从加载阶段就非常空闲LCP 能提前完成CLS 也更容易控制交互响应更快。你说它是“魔法”其实底层就是“不干活就不背锅”。2. 群岛架构的解剖Astro 到底有什么魔力2.1 “岛屿”是怎么冒出来的群岛架构这个概念并不是 Astro 团队凭空发明的它最早由前端大佬 Jason Miller 提出核心思想是页面上大部分区域是静态的只有少数“活”的区域需要客户端 JavaScript我们应该把资源聚焦到这些活区域上。Astro 把这个理念落到了产品层面。你写一个组件时可以像写普通 HTML 一样但如果你想让它成为“岛”只需要在使用它时加一个client:load指令。听起来很玄实际上就是这么简单。比如--- // 引入一个导航栏组件 import Navbar from ../components/Navbar.jsx; --- !-- 这是一个静态组件默认不输出 JS -- Navbar / !-- 这也是同一个组件但加上了交互指令会输出 JS 并在浏览器加载 -- Navbar client:load /同一个组件一个用在静态区域一个用在可交互区域。Astro 会为后者构建一个“水合包”前者则完全不打进产物里。这就是 Astro 与 Next.js 在思路上的一个巨大差异Next.js 是靠“整个应用”的约定来做优化Astro 是颗粒度极细地控制每一个组件的客户端行为。2.2 页面上常见的“岛”有哪些拿一个典型的技术博客来说整篇文章的内容、目录、标题、表格这类信息展示区域完全不需要 JS而导航栏的汉堡按钮、搜索框的实时过滤、评论区、暗色模式切换、订阅表单的校验这些才需要客户端交互。Astro 默认让你把这些“活”组件一个个变成岛而不是把所有页面代码一股脑加载进去。我在自己的博客里改造过一版统计了一下整站 12 个页面所有交互集中于搜索弹窗、目录高亮、主题切换和评论区四个地方。这些组件加起来打包后的 JS 总量约 35KB而且分散成四个独立文件按需加载。原来的 React 版博客首屏直接加载 200 多 KB 的 JS还得等着 React 自行水合差距就是这么来的。2.3 不要为了“零 JS”牺牲了动态性有人会说Astro 是“静态站点生成器”那我就只能做纯静态页面了吗其实不是。Astro 同时支持服务端渲染SSR你可以把它部署到支持 Node 或者边缘函数的平台上页面也能跑动态逻辑。你可以在.astro文件里写一段服务端代码从数据库读取数据在构建时或请求时渲染输出。注意一个关键词时分复用的“时”。Astro 会在合适的时机决定输出静态 HTML 还是由服务端动态生成。你可以在“构建时”就确定的数据彻底静态化在“请求时”才确定的数据用 API 接口或export const prerender false来标记。这种灵活性反而比很多“半吊子 SSG”更优雅。2.4 和 Next.js 的“静态优化”相比有什么本质区别Next.js 也有静态生成能力一个页面如果没用到动态 API在构建时也会被输出成 HTML。那两者的差异在哪里差异在于“客户端水合的粒度”。Next.js 的静态生成实际上是“静态 HTML 全局水合”。只要页面里挂了一个用了 React 的交互组件整个应用就得加载一遍 React 运行时所有客户端组件都得被解析和初始化。Astro 则是把一个交互组件当作独立的“微前端岛”用哪个岛就加载哪个岛每个岛可以有自己独立的运行时甚至可以同时混用 React、Vue、Svelte而不彼此冲突。我认识一个工程师他在 Astro 里根目录导航用 Svelte 写评论区用 Preact 写数据可视化用 Vue 写三个框架共存完全没有问题。这种“多框架共存的自由”在 Next.js 里想都不敢想但在 Astro 里就是日常操作。3. 实测同一个博客Next.js vs Astro3.1 测试环境与内容设计为了避免“夸 Astro 踩 Next.js”的嫌疑这次实测我严格遵守了公平原则两套代码使用同一份内容素材同一个设计稿同一个部署平台同一套缓存策略。博客包含 5 篇文章每篇文章约 3000 字配有若干代码块和图片占位。页面上有导航栏、文章列表页、文章详情页、标签页。交互需求导航栏在移动端需要折叠展开目录需要滚动高亮评论区有一个简单的复选框联动。Next.js 版本用 App RouterReact 18默认开启 Streaming 和 Suspense构建产物用next build输出。Astro 版本用最新稳定版交互组件用 React 实现并加client:load静态内容全部默认输出。两套代码都部署在 Vercel 上使用相同的边缘网络区域。我用 Lighthouse 模拟了 Moto G Power 设备、4G 慢速网络环境Fast 3G 模拟跑分同时还用 WebPageTest 记录了资源请求瀑布图。3.2 核心指标对比差距比想象中还离谱最终数据整理成一张表很多数字我都反复验证过结论非常稳定指标Next.js 版本Astro 版本差异首屏传输体积HTMLJSCSS约 862 KB约 42 KB减少 95%首屏 JS 请求数9 个1 个且未阻塞渲染减少 89%LCP最大内容绘制3.8 秒1.2 秒提升 68%TBT总阻塞时间620 毫秒0 毫秒完全消除CLS累计布局偏移0.050.01更稳定Lighthouse 性能得分83100提升 20%最夸张的一次测试中Astro 版本的首屏 HTML 只有 8KB文章页几乎全是文本而 Next.js 版本仅 JS 文件就发出了 800 多 KB 的请求React runtime 是重头。首屏速度提升 100% 并不是噱头是实打实的结果。阅读体验上更是云泥之别Astro 版本的页面几乎是一秒内完整呈现而 Next.js 版本在弱网下能明显看到“白屏—内容闪现—按钮可点”三个阶段。3.3 为什么 Next.js 会这么“重”Next.js 的“重”不是因为它烂而是因为它解决的问题本身就是“重”的。它要处理路由、数据变化、客户端状态、流式渲染、懒加载、缓存更新这些机制在做复杂应用时是刚需。但代价是基础消耗高任何一个交互都需要完整运行时。有人可能会说“我用 Next.js 静态导出不就不需要 React runtime 了吗”理论上可以但实际操作中你会发现只要用了next/link的预取或者任何一个客户端组件路由层和 React 还是很霸道地占据大量的字节。不信你开一个空的 Next.js App Router 项目next build之后在浏览器控制台看看 Network 面板next/dist/client相关的 chunk 一定会出现。Astro 不为无用的交互买单它甚至可以把链接预取任务交给浏览器原生功能prefetch或者干脆什么都不做让用户在点击链接时直接请求下一个 HTML 页面。这种方式对内容站来说完全够用而且体验上没有任何损失。3.4 别忽视“编译时间”这个隐性成本构建速度也是选型时容易被忽略的指标。Next.js 在做静态生成时需要把每个页面都水合一遍来获取具体的数据和 HTML构建耗时随页面数线性增长。Astro 的构建则近乎“复制粘贴”它直接输出静态字符串几乎没有水合开销。我这 5 篇文章的小项目Next.js 完整构建耗时约 57 秒Astro 约 8 秒。放到几百篇文章的博客上这个差距会非常明显。Astro 甚至有一个“零水合构建”模式纯内容页面完全不走客户端逻辑构建时间几乎不受页面数量影响。所以如果你准备做一个文档站或者内容站且内容量偏好大Astro 的构建优势会进一步放大。4. 实操从零开始用 Astro 搭建一个高绩效博客4.1 初始化项目与目录结构这部分内容我会带你走一遍完整的实际操作流程全程可复现。首先创建一个新项目npm create astrolatest在交互式命令行里选择Empty模板后续是否使用 TypeScript 看你习惯我建议选是。进入项目后目录结构大体是src/ components/ # 各种组件支持 .astro .jsx .vue .svelte layouts/ # 布局组件比如博客文章的外壳 pages/ # 页面路由使用文件系统路由 content/ # 内容集合可以用 Markdown/MDX 写文章 public/ # 静态资源 astro.config.mjs # Astro 配置文件看到这里熟悉 Next.js 的同学应该觉得似曾相识Astro 的文件系统路由方式和 Next.js 非常像过渡成本极低。4.2 写一个普通的页面组件一个.astro文件同时包含组件逻辑顶部使用---包裹的代码块和模板两部分类似 Vue 的 SFC 单文件组件。比如--- // 这里是服务端执行或构建时执行的代码 const siteTitle 我的技术博客; const posts [ { title: 文章一, url: /posts/1 }, { title: 文章二, url: /posts/2 }, ]; --- html langzh-CN head meta charsetutf-8 / title{siteTitle}/title /head body h1{siteTitle}/h1 ul { posts.map(post lia href{post.url}{post.title}/a/li) } /ul /body /html注意src/pages/index.astro对应网站的首页src/pages/posts/[slug].astro对应文章动态路由。[...]这种写法在 Astro 里也叫“动态路由”和 Next.js 非常相似。4.3 给页面添加“岛屿”组件假设我们想加入一个搜索框它会在用户输入时实时过滤文章标题。这个交互功能需要客户端 JS 才能完成。我们直接写一个 React 组件然后引入到 Astro 页面里--- import SearchBox from ../components/SearchBox.jsx; --- !-- 关键点client:load 指令让该组件变成“岛” -- SearchBox client:load posts{posts} /client:load的意思是页面加载完成后立即加载并水合这个组件。还有几个常用的客户端指令我列一下client:idle浏览器空闲时再加载适合优先级较低的交互组件。client:visible组件滚动进入视口后才加载非常适合长页面底部的内容。client:media满足指定媒体查询条件时才加载比如移动端菜单只在窄屏显示时加载。client:only只允许在客户端渲染某些依赖 window 对象的第三方库必须用这个。选指令时我个人的习惯是能不用就不用必须用尽量用client:visible或client:idle把优先级让给首屏核心内容。4.4 使用 Content Collections 管理文章之前 Astro 推荐直接用 Markdown 文件加 header现在更标准的是 Content Collections它提供类型检查和更清晰的内容结构。在src/content/blog/下放你的文章文件比如hello-astro.md--- title: Hello Astro description: 这是我用 Astro 写的第一篇文章 date: 2025-01-15 tag: [前端, Astro] --- 正文内容……然后在你需要展示文章列表的地方用getCollection读取--- import { getCollection } from astro:content; // 构建时或服务端运行时读取内容 const posts await getCollection(blog); --- { posts.map(post ( article a href{/posts/${post.slug}/}{post.data.title}/a p{post.data.description}/p /article )) }这个体验真的非常顺甚至比很多成熟 CMS 的编辑器还好用。你在本地写 Markdown改完刷新就能看到效果构建时所有文章都会被静态化成独立的 HTML 文件。4.5 部署一行命令上生产Astro 的部署极其简单因为大部分输出是纯静态文件。如果部署到 Vercel、Netlify、Cloudflare Pages 这些平台只需连上仓库平台会自动识别 Astro 项目并执行构建。如果你自己有一台服务器也可以直接npm run build之后把dist目录里的静态文件扔到 Nginx 或者 CDN 上。如果是部署到 Node 环境需要 SSR记得在astro.config.mjs里配置 adapter// astro.config.mjs import { defineConfig } from astro/config; import node from astrojs/node; export default defineConfig({ output: server, adapter: node({ mode: standalone }) });不过我的建议是干净的内容站优先用静态生成需要动态的部分用“岛屿”方案加 API 接口解决别轻易引入 SSR。SSR 带来的复杂度对内容网站通常是负收益。5. 什么时候该继续用 Next.js什么时候该投入 Astro 怀抱5.1 适合 Astro 的场景先聊聊适合上 Astro 的站点类型我按自己接触过的项目排个序技术博客、个人博客、公司新闻中心产品官网、落地页、活动页面开源项目文档站比如 Vitest、Vite 的部分文档页面就在用类似方案企业站、宣传站内容以文本和图片为主少量交互电商商品详情页的外壳部分交互密集的购物车和结算可以独立成应用或岛这类站点的共性是什么用户访问路径通常是“看内容、获取信息、点击导航”而不是长时间停留在页面上操作一堆按钮。页面越接近“文档”Astro 越能发挥优势。在我的体验里阿斯托的“multipage app”思维在做 SEO 和分享场景时也特别舒服每个页面都是独立的 HTML社交爬虫不会因为 JavaScript 没执行就抓不到内容这是 SSR 和 SSG 的天然优势而 Astro 把这个优势放大到了极致。5.2 哪些场景硬上 Astro 会摔跟头Astro 也不是银弹它的短板主要体现在这几类诉求上完整的客户端应用状态管理比如一套由用户操作驱动的复杂仪表盘实时双人/多人协作类似在线文档、白板、多人编辑工具需要频繁路由变化而不刷新整个页面的富交互产品比如后台管理系统依赖大量全局状态和 UI 组件库的 SPA 项目拿一个后台管理系统举例你登录后需要维护一张几百行的数据表格每一行都有下拉菜单、弹窗、表单校验还要实时和服务器同步。这种场景你用 Astro 硬写虽然表面上也能实现但状态管理、路由缓存、数据同步的控制权全在自己手里代码会迅速膨胀反而比直接用 React/Vue 全家桶更痛苦。Next.js 在这种场景下的价值就体现出来了它提供了 React Server Components、App Router、Server Actions 等一整套一体化方案状态和路由无缝配合。这是深度框架的取舍。5.3 选型决策表一张表看明白需求特征推荐方向理由内容驱动、SEO 迫切、访问速度敏感Astro零 JS 默认输出构建快页面极轻核心功能完全在浏览器内交互复杂Next.js/React客户端水合生态成熟状态管理工具丰富需要多框架混用Astro岛屿可独立加载不同框架需要高频发布、动态路由变化多Next.js增量静态再生成ISR机制更成熟团队全是 React 开发期望统一技术栈Next.js一套代码解决全部问题如果你做的事情是“一半内容展示 一半后台管理”那完全可以分两套系统内容前台用 Astro管理后台用 React/Next.js两者通过 API 对接。这不算拆分过度而是一种务实的“外科手术式”架构。6. 常见问题与排查技巧实录6.1 “岛屿”怎么不加载组件明明写了 client:load我自己踩过的最大的坑就是写了 React 的组件也加了client:load但打开浏览器一看 Network 面板里就是没有对应请求。排查了半天才发现是忘记在astro.config.mjs中加入 React 集成。import react from astrojs/react; export default defineConfig({ integrations: [react()], });如果缺少这一行.jsx文件被当成了纯静态内容组件里的useState、onClick等等全部失效页面不报错但就是没有交互。遇到任何“组件没有效果”的问题先检查集成配置是否完整。6.2 构建时内容加载失败或者数据异常Astro 的页面组件默认在构建时执行如果你的页面里有从远程 API 获取数据的代码且网络不稳定构建可能会失败。解决的思路有三个把远程数据源封装成接口构建时用缓存兜底改为export const prerender false让页面在请求时渲染把数据获取放到客户端组件里用client:load加载我的建议是能静态就静态能缓存就缓存实在不行才走客户端请求。这也是 Astro 官方文档强调的最佳实践。6.3 尾部的动态路由getStaticPaths要写全使用动态路由[slug].astro时如果没提供getStaticPathsAstro 构建阶段会报“无法生成动态页面”的错误。一个典型的写法是--- export async function getStaticPaths() { const posts await getCollection(blog); return posts.map(post ({ params: { slug: post.slug }, props: { post }, })); } const { post } Astro.props; ---这里有一个容易忽略的点如果未来有新增文章但构建时没有在getStaticPaths里返回对应的slug那这个页面不会自动生成链接。在本地开发时新增文章很可能需要重新构建才能看到新路径这是静态站点的预期行为不算 bug。6.4 想用组件库但不想全量引入有人喜欢用现成的 UI 库比如shadcn/ui或者Headless UI在 Astro 里也可以用。核心技巧是把 UI 库的“客户端组件”单独拆成岛屿来引用。以shadcn/ui的Dialog为例--- import { Dialog } from /components/ui/dialog; --- Dialog client:visible button打开弹窗/button /Dialog这个时候Dialog的依赖只会在它真正进入视口时才加载其他静态内容依然保持干净。我也实测过一个只用了按钮、下拉菜单、弹窗的官网在 Astro 里的客户端载荷只有 15KB 左右而同样的页面在纯 React 项目里无论如何都要 300KB 起步。6.5 线上页面打不开或者白屏先关掉 JS 试试遇到白屏绝大多数时候是某个客户端组件运行时抛了异常。我的排查方法是在浏览器里先禁用 JavaScript重新加载页面。如果内容能正常显示那问题基本锁定在某个“岛屿”组件上。接着逐个清除client:指令直到找到出问题的那个组件。这个思路在日常调试里非常好用尤其是在引入第三方 React 库时对方可能在服务端渲染环境下偷偷调用了document或window导致水合失败。这类兼容性 bug 在 Astro 中常见的解法是用client:only强制客户端渲染确保第三方组件只跑在浏览器安全区。最后分享一点我自己的感受用 Astro 做了三个月的个人网站重构之后我最大的变化是写页面时的心态彻底翻转了。以前写 Next.js 时我潜意识里把每个页面都当成了一个需要水合的“React 应用”每个链接都要考虑路由预取每次交互都想着怎么状态管理。但用 Astro我默认先问自己这里真的需要 JavaScript 吗大多数答案是不需要这会逼着你把交互做得更克制、更合理。这套思维模式对我做任何前端项目都有帮助。它提醒我技术选型不是追新而是搞清楚你服务的用户、内容形态和核心指标。那些吹得天花乱坠的框架落地到你的具体场景里可能只是个精致的摆设。好了这篇“群岛架构”实战就聊到这里。如果你也在考虑把某个内容站从 Next.js 迁到 Astro或者手上有一个新项目正在纠结选型欢迎照着上面的步骤试一遍。数据自己会说话。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价