资讯动态

从Docusaurus到Astro 5.x:Islands架构与静态站点性能优化实战

发布时间:2026/10/15 9:00:04 来源:尧图企业网站定制
迁移前的思考为什么要放弃 Docusaurus转向 Astro 5.x我最早接触 Docusaurus 3.x 时对它印象很好。文档站该有的功能它几乎全覆盖侧边栏、版本管理、全文搜索、MDX 支持、暗黑模式开箱即用团队上手成本极低。对一个以内容输出为主的站点来说Docusaurus 3.x 确实是省心之选。但随着项目里的 React 组件越来越多、页面交互越来越重我开始频繁遇到几个绕不开的痛点。最明显的是产物体积和运行时开销——Docusaurus 本质上是一个 React SPA 外壳首屏要下载整个应用框架、路由表和大量 JavaScript 包哪怕用户只想读一段纯文本浏览器也得先执行一波框架初始化逻辑。我当时的站点首页只有不到 20KB 的正文内容加载的 JS 却接近 500KB这种比例显然不正常。构建速度是第二个触发点。Docusaurus 3.x 底层走 webpack 5冷启动一次全量构建中等规模的文档站也需要 40 秒以上。内容更新后频繁 rebuild开发体验被打断得很厉害。我一直在想文档站这种场景核心诉求明明是“静态内容 少量交互”为什么要把整个页面都交给 JavaScript 处理后来我接触到 Astro 5.x才意识到答案早就有人做好了静态页面用岛屿架构交互组件按需水合。把 500KB 全量 JS 拆成 5KB 的独立岛屿之后性能数字和构建时间都彻底改变。这篇文章我会完整记录从 Docusaurus 3.x 到 Astro 5.x 的迁移过程重点讲清楚 Islands 架构如何在同一套组件体系下实现性能和构建速度的双重提升以及那些迁移说明文档里几乎没人写透的坑。如果你也在用 Docusaurus 或者其他 React 系静态站工具并且正在纠结要不要切换这篇文章值得你耐心看完。下面所有步骤、配置和踩坑记录都是我实际跑过迁移后整理出来的。1. 核心差距分析Docusaurus 3.x 与 Astro 5.x 的定位差异1.1 Docusaurus 的强项与硬伤Docusaurus 是一个文档优先的静态站点生成器它最大的优势不在技术实现而在“约定优于配置”的聚合体验。安装后只需要写 Markdown侧边栏会根据目录结构自动生成版本控制有现成插件团队协作时的认知成本非常低。这些能力非常适合技术文档、团队知识库、API 手册这类以文字为主的内容站。硬伤也很明确。第一它是单页应用架构虽然构建时输出 HTML 文件但浏览器加载后会把页面升级成 SPA所有页面切换都走客户端路由路由状态、组件生命周期、React 运行时要始终常驻。第二全站只有一个统一的入口 JS bundle即使某个页面没有任何交互也必须加载框架代码。第三主题自定义高度依赖 React 组件想改一个简单的头部导航得理解它内部的组件层级和 CSS Modules 结构普通内容维护者根本改不动。第四构建链路的 webpack 配置非常重对大型项目的二次优化空间很小缓存机制也不够透明。判断一个站点工具是否合适重要的是区分它的强项是否匹配你的实际场景。如果你的站点就是纯文档没有复杂交互Docusaurus 的这些硬伤会被内容优势盖过没必要迁移。但像我这种需要统计图、动态表格、目录树筛选、可交互示例的文档站Docusaurus 就力不从心了。1.2 Astro 5.x 的设计哲学与内容层能力Astro 5.x 的核心思路完全反过来默认所有页面都是纯静态 HTMLJavaScript 默认不输出。只有你显式声明的组件才会被“水合”成客户端可交互的岛屿。这种 MPA 多页面架构让每一个页面都只携带自己需要的内容和依赖不存在全站共用的重型运行时。Astro 5.x 最值得关注的是 Content Layer 内容层重构。你可以用defineCollection和loader定义内容的来源无论是本地 Markdown 文件、远程 API 还是 CMS 接口都能以统一的 schema 结构加载。这个设计对迁移非常友好因为 Docusaurus 的文档结构本质上是“Markdown 文件 文件夹层级 Frontmatter 元数据”这些资产在 Astro 内容层里可以被近乎无损地承接过来。Astro 5.x 的另一个特色是默认使用 Vite 6 作为构建内核。相比 webpackVite 的开发服务器基于原生 ESM冷启动和热更新速度快一个量级。构建阶段用 Rollup 做产物打包配合细粒度的代码分割JavaScript 产物会被拆成小模块只有真正需要的页面才会加载对应模块。这为后面讲性能对比和构建速度提升做了坚实基础。1.3 Islands 架构为什么能在文档场景生效Islands 架构常被翻译成“岛屿架构”它把页面看成一片由静态 HTML 构成的海洋只有交互组件是漂浮在海面上的岛屿。在 Docusaurus 的世界里整个页面是一个巨大大陆每一条电路都通着大陆电源任何无关组件的水合开销都由访问者买单。在 Astro 里每个岛屿是独立的有自己独立的 JavaScript 加载和水合生命周期大海的部分保持完全的静态。对于文档站来说绝大多数页面内容不需要 JavaScript正文、标题、代码块、表格、图片、侧边导航全是静态展示。真正需要交互的部分通常只有搜索框、折叠面板、主题切换、评论区、图表控件。Islands 架构精确匹配这种“内容多、交互少”的比例。我做了一个很朴素的比喻Docusaurus 像是开一家把整个厨房都搬到每张桌子旁边的餐厅顾客只是点了一杯水但后厨的灶台、冰箱、抽油烟机全都得先通电。Islands 架构则像中央厨房只把顾客真正需要的菜送到桌边。这不仅仅是加载体积的节省更影响移动端低性能设备的用户体验。Astro 在整页静态输出的同时对每个岛屿可以采用不同的水合策略比如组件进入视口时才加载或者浏览器空闲时才加载这在后面实操部分我会详细展开。2. 迁移路线图从 Docusaurus 3.x 到 Astro 5.x 分步指南2.1 资产盘点与准备工作动手之前先把家底盘点清楚。迁移最忌讳的就是边改边删最后东西都找不回来了。我通常会先建一个迁移清单分四类整理内容文件、静态资源、定制组件、全局配置。内容文件主要是 docs 和 blog 目录下的所有 Markdown/MDX 文件。Docusaurus 和 Astro 对 Markdown 的解析大体一致但 frontmatter 字段有差异。Docusaurus 常用的字段包括sidebar_position、slug、tags、custom_edit_url这些字段在 Astro 内容层里不会自动生效需要显式定义 collection schema或者在迁移过程中有选择地丢弃。我从实践中发现slug字段必须保留因为它直接决定了最终 URL 路径保留它可以避免线上链接全部失效。sidebar_position字段在 Astro 里没有对应语义需要转换成 Astro 侧边栏配置里的排序权重。静态资源一般放在static/目录下迁移时直接整体复制到 Astro 的public/目录路径引用几乎不变。定制组件则是最费时间的一块。Docusaurus 的自定义组件基本都是 React 组件而 Astro 中你可以继续使用 React但需要额外安装集成插件例如astrojs/react。如果组件不多我建议直接用.astro文件重写因为 Astro 组件的模板语法更简洁而且可以避免不必要的客户端运行时依赖。2.2 初始化 Astro 5.x 项目骨架迁移过程中我没有直接在原项目里原地修改而是先创建了一个平行的新项目。这样做的好处是旧站点可以继续保持可用新站点的页面搭建好一部分、验证一部分再切换流量。创建 Astro 项目的命令很直接npm create astrolatest my-blog -- --template minimal cd my-blog npm install注意--template minimal会生成极简目录不包含任何 UI 框架方便后面按需集成。接着安装内容和集成相关的依赖npm install astrojs/react astrojs/mdx astrojs/sitemap astrojs/rss这里多说一句Astro 的官方文档站主题是 Starlight它本身就是基于 Astro 构建文档站的解决方案集成了侧边栏、搜索、分页、代码高亮等文档站点需要的全套能力。如果你的项目定位是纯文档站并且没有太多特殊交互组件直接使用 Starlight 会省掉很多工作。但如果像我们这样已经有 Docusaurus 站点且定制了大量 React 组件我更建议用基础 Astro 项目自己组装因为 Starlight 的主题定制同样需要写 Astro 组件绕一圈不如直接在内容层上搭建自己的页面骨架。创建好项目后需要完善astro.config.mjs配置。我的初始配置长这样import { defineConfig } from astro/config; import react from astrojs/react; import mdx from astrojs/mdx; import sitemap from astrojs/sitemap; export default defineConfig({ site: https://your-domain.example, integrations: [react(), mdx(), sitemap()], output: static, build: { inlineStylesheets: auto } });site字段一定要设置否则 sitemap 和 canonical 标签无法正确生成。output: static是 Astro 5.x 的默认输出模式构建时直接生成静态 HTML这对文档站是最优解。如果你有动态路由需求可以换成output: server但性能会打折扣我建议能用静态就不用服务端。2.3 内容集合层把 Markdown 变成可查询的数据Astro 5.x 内容层最核心的用法是在src/content.config.ts里定义集合。Docusaurus 里文档和博客是两个清晰的分类迁移时保持同样结构即可。我的定义样本import { defineCollection, z } from astro:content; import { glob } from astro/loaders; const docs defineCollection({ loader: glob({ pattern: **/*.{md,mdx}, base: ./src/content/docs }), schema: z.object({ title: z.string(), description: z.string().optional(), slug: z.string().optional(), tags: z.array(z.string()).optional(), order: z.number().optional(), }), }); const blog defineCollection({ loader: glob({ pattern: **/*.{md,mdx}, base: ./src/content/blog }), schema: z.object({ title: z.string(), pubDate: z.coerce.date(), description: z.string().optional(), updatedDate: z.coerce.date().optional(), }), }); export const collections { docs, blog };这个 schema 设计有讲究。slug字段允许目录结构和 URL 路径解耦解决了 Docusaurus 文档链接迁移后的兼容问题。order字段是为了把原来的sidebar_position换成更中性的排序标记。如果你的文档里还有其他自定义 frontmatter 字段比如authors、video、download_url建议每添加一个需求都在 schema 里声明一次。Astro 的内容层会在构建时做类型校验字段缺失或类型错误会直接报错比 Docusaurus 的宽松模式严格得多这反而是好事能提前发现内容质量问题。2.4 页面模板和布局替换的注意事项内容文件迁移好后需要把它们渲染成页面。在 Docusaurus 里文件路径直接对应路由侧边栏由插件自动生成。Astro 则需要在页面文件里显式查询内容集合。以文档页面为例我创建了一个动态路由页面src/pages/docs/[...slug].astro--- import { getCollection, render } from astro:content; import Layout from ../../layouts/DocsLayout.astro; const { slug } Astro.params; const docs await getCollection(docs); const page docs.find((doc) doc.id slug); const { Content } await render(page); --- Layout title{page.data.title} article classdoc-content Content / /article /Layout这看起来简单但实际会遇到几个问题。第一原来的/docs/getting-started可能对应docs/getting-started.md如果项目里还有docs/getting-started/index.mdxDocusaurus 会优先用 index 文件Astro 则需要你统一配置 glob 的base路径协调好文件组织方式。第二URL 结尾是否带斜杠Docusaurus 3.x 默认开启 trailing slashAstro 默认不带这会导致旧链接 404。解决方法是统一在路由里生成规范化的链接并且在astro.config.mjs里设置trailingSlash: always来保持旧链接兼容。页面布局也需要重写。Docusaurus 的布局里包含全局搜索框、侧边栏、footer、主题切换这些原本是 React 组件渲染的现在可以作为 Astro Layout 组件的插槽结构。我把头部导航、侧边栏目录、收藏夹按钮等静态部分做成了.astro组件主体内容通过slot /插入。这个转换过程比想象中麻烦因为 Docusaurus 的样式是靠 CSS Modules 和 CSS 变量组织起来的迁移到 Astro 里最好顺带换成你熟悉的全局 CSS 方案。3. Islands 架构实战按需水合的核心操作3.1 筛选出真正需要 JavaScript 的组件迁移完静态内容之后最关键的一步就是处理交互组件。Islands 架构的价值要在这一步真正落地。我按组件的交互特征把它们分成三类完全纯静态的一部分例如标题、段落、静态表格、图片需要少量浏览器 API 实现交互的一部分例如代码复制按钮、折叠面板、主题切换复杂数据驱动的一部分例如图表、树状目录筛选、实时搜索。对于第一类直接停留在静态 HTML不引入任何水合。这一条看似废话但实际操作中很多人会把导航菜单、面包屑等本可以纯静态的部分也做成客户端组件这违背了 Islands 的核心精神。对于第二类优先考虑原生 HTML 实现。折叠面板可以用details和summary主题切换可以用 CSSprefers-color-scheme结合 localStorage 脚本这些根本不依赖框架。只有真正复杂的组件才值得作为 Astro 页面里的岛屿存在。我给的判断标准非常简单组件是否需要在多个页面里共享一套复杂状态需要则做成岛屿不需要就优先考虑原生方案。迁移后的站点里我原本 12 个 React 组件最终只有 4 个被保留为客户端岛屿其余全部被原生实现或静态结构替代页面平均 JavaScript 体积骤降 90%。3.2 用 client 指令精确定制水合时机Astro 提供了好几种客户端指令它们决定了岛屿展示给浏览器的方式和时间。client:load是页面加载后立即加载组件脚本并水合适合首屏就要出现的核心交互比如搜索框。client:idle是浏览器空闲时才加载适合不着急的组件比如页脚的评论模块。client:visible是组件进入视口区域后才开始加载适合长页面里的图表和目录树。client:media则可以按媒体查询条件加载比如只在移动端显示时加载特定组件。client:only表示完全在客户端渲染如果你要把某个组件推送到不参与构建的浏览器环境时使用。我在文档站里的用法是搜索框用client:load因为用户可能一到页面就使用搜索文章目录TOC高亮插件用client:visible因为要等滚到对应位置才需要计算评论区和数据分析图用client:idle不影响首屏加载。这个组合实践下来首屏 HTML 渲染完成后浏览器只需要执行搜索框相关的少量脚本其余脚本都延后或跳过。段落里顺带提一个容易忽略的点client:visible依赖 IntersectionObserver 机制如果你要支持比较老的浏览器需要额外引入 polyfill。我一开始忽略了这个问题导致内部测试用户在低版本浏览器上反馈目录高亮不出现排查了半天才发现是 polyfill 缺失。3.3 水合成本的真实对比从打包数据看效果聊了这么多理论我实际迁移完成后做了打包数据对比。原 Docusaurus 站点的首页产物情况是HTML 约 9KBJS 总量约 486KBCSS 约 68KB。其中 JS 包含 React、ReactDOM、路由逻辑、Docusaurus 运行时还有首屏根本用不到的组件代码。切换到 Astro 5.x 后同样一个首页HTML 约 8KBJS 总量约 14KBCSS 约 35KB。这 14KB 里还包括了主题切换脚本和搜索框核心代码。用 Lighthouse 跑移动端模拟性能得分从 71 分上升到 96 分最大内容绘制时间从 2.8 秒降到 0.9 秒。总阻塞时间从 640 毫秒降到 30 毫秒。这些数字没有经过任何炒作就是默认配置下的实测结果。这个提升的本质并不是 Astro 比 Docusaurus 更“聪明”而是它没有强迫你加载你不需要的东西。静态内容就是静态内容浏览器可以直接渲染不用等 JavaScript 解析执行。还有一个容易被忽略的收益是 SEO 相关。Docusaurus 虽然也能输出静态 HTML但页面内大量内容由客户端渲染搜索爬虫如果禁用 JavaScript抓取效果会打折扣。Astro 默认纯静态输出的 HTML 里就是完整正文每个页面的内容都会完整呈现在初始 HTML 中这保证了搜索爬虫无论是否执行脚本都能拿到完整文本。这一点对文档站尤其重要因为文档站很大一部分流量来自搜索引擎。4. 构建速度与产物体积的深度调优4.1 构建链路对比webpack 5 与 Vite 6 的差距在哪里构建速度升级的核心来自底层的模块打包工具差异。Docusaurus 3.x 沿用 webpack 5开发模式启动时要把所有模块编译打包进一个 bundle项目规模一大冷启动和热更新都会衰减。Astro 5.x 使用 Vite 6。Vite 在开发模式充分利用浏览器原生 ESM按需编译当前请求到的模块冷启动只在毫秒级别完成模块图加载。这就像一个是每次都重新打包一整个集装箱另一个是按需输送单个零件。我记录过一次实际数据同样的机器、同样的内容规模Docusaurus 开发服务器冷启动约 8 秒页面热更新平均 800 毫秒Astro 开发服务器冷启动不到 1 秒页面热更新平均 80 毫秒以内。这个开发体验差异对长期维护项目的团队来说非常明显。生产构建方面Astro 5.x 的 Rollup 打包过程同样优于 webpack。因为 Astro 默认页面级代码分割每个页面只包含自己的静态内容和岛屿脚本所以构建时每个页面的依赖计算可以并行执行。我的项目里Docusaurus 生产构建进行约 42 秒Astro 生产构建约 11 秒内容量相近。这个数字会随组件复杂度变化但方向是确定的Astro 的构建链对静态内容站点做了专项优化不会背负一个 SPA 框架的全部依赖。4.2 增量构建与缓存策略在实际迭代中比冷启动更影响心情的是热更新时间。Docusaurus 的 webpack 在监听模式下虽然有缓存但内容和组件之间的关系绑定较深修改一个侧边栏配置可能导致整个导航逻辑重新编译。Astro 的内容层则能做到更细的监听粒度修改一个 Markdown 文件时只有对应页面的模板重新渲染其他页面不会重跑。Astro 构建时还可以启用网站缓存。我在 CI 流程里会保留构建缓存目录和依赖目录这样下一次构建时未变动的模块可以直接复用缓存结果。实测下来CI 上第二次构建能从 11 秒再压到约 5 秒。如果你用 GitHub Actions 或者类似平台配置缓存路径时一定要把astro的缓存目录加进去。4.3 图片和静态资源处理的差异图片处理是构建速度优化里最容易踩坑的一块。Docusaurus 3.x 对图片不做统一优化图片放在 static 目录直接原样输出。Astro 5.x 内置的astro:assets组件提供了响应式图片、AVIF/WebP 格式转换、尺寸缩放等能力而且处理发生在构建阶段不占运行时开销。这里我强烈建议把站点的图片资源迁移到src/assets目录而不是留在public。因为public目录里的图片是原样复制不做任何优化src/assets里的图片经astro:assets处理后能自动生成多尺寸版本。我的文档站里有大量截图和架构图迁移前单张截图可能 600KB迁移后自动转成 WebP 并压缩到约 120KB页面整体传输体积又下了一个台阶。构建时图片处理是全并行执行的几十张图片追加的时间在几百毫秒内完全可接受。4.4 体积专项优化从产物反推组件水路设计产出体积和性能不是玄学可以从构建产物反推优化方向。我构建后会把dist/目录按 JS 文件大小排序看看最大的那几个脚本是哪些组件产生的。Astro 5.x 会在每个 JS chunk 文件名后带上 hash通过浏览器开发者工具的 Network 面板点击那个 JS 文件就能知道是哪部分代码的大型 node_modules 依赖被打包进来。我迁移后发现有两大块额外体积一个是文档站里的编辑器和代码高亮库另一个是图表库的地图渲染模块。图表库的默认包会把所有图表类型都打进来但站点只需要折线图和柱状图。解决方法是改用按需导入或者直接换更轻量的图表方案。编辑器功能通过client:idle加载切到浏览器空闲后才会下载不影响页面首屏指标。这一轮优化后站点所有页面的平均 JS 总量从 14KB 降到 11KB 左右我自己相当满意。5. 常见问题排查与踩坑实录5.1 链接策略差异导致的 404 洪水迁移后最容易瞬间爆发的问题就是链接失效。Docusaurus 的链接书写方式支持相对路径比如在docs/getting-started.md里写[API 说明](../api/overview)Astro 的 Markdown 里默认用源码路径解析两者语义不一致。我在迁移批量替换时先把所有 Markdown 里的相对链接统一改成绝对路径以/docs/开头这样就避开了不同文件层级带来的解析差异。另外值得注意是 Docusaurus 对slug字段的语义解析。原来是slug: /foo/bar迁移到 Astro 里要确保[...slug].astro的slug参数一致。我在迁移时建了一组 URL 映射测试脚本批量生成旧站的所有页面链接再与新站链接做比对手动修复掉所有不一致的项。这一步做完站点的 404 率直接清零。5.2 客户端组件里访问 window 对象报错迁移完运行组件时最常见的报错是window is not defined。原因是 Astro 构建阶段会预先执行组件代码提取静态标记如果你的组件在最顶层访问了window或document构建进程里没有这些对象直接报错。我一度以为是 React 集成配置问题排查了很久。解决方案分两层。第一层把访问浏览器 API 的代码全部移动到useEffect或事件回调里不要在模块顶层直接调用。第二层如果组件确实依赖浏览器全局变量可以在执行前判断typeof window ! undefined或者使用client:only指令告诉 Astro 这个组件完全不需要在构建阶段执行只走客户端渲染。用client:only时要注意组件在首屏会有短暂的空渲染阶段不适合需要闪帧展示的内容。5.3 侧边栏和导航组件重写的关键细节Docusaurus 的侧边栏自动生成功能很强大迁移到 Astro 后你需要自己实现同样的逻辑。我建议在内容集合里引入一个order字段来控制排序再在Layout组件里遍历全部文档生成侧边栏结构。这里有一个容易忽略的细节Docusaurus 默认会按文件夹层级生成侧边栏分类Astro 里没有这个层级概念因为所有 Markdown 扁平地放在一个 collection 里。我用文件路径的前缀作为分类标识并在侧边栏组件里做分组渲染。迁移后的侧边栏还支持了展开、折叠这部分我用原生details实现了全部交互所以侧边栏不需要任何 JavaScript 岛屿加载时零脚本成本。如果你需要更高级的目录筛选那再考虑引入岛屿组件。5.4 搜索功能迁移的替代方案Docusaurus 3.x 官方推荐接入 Algolia DocSearch但这对私有文档站或者要求数据不离本地的场景并不友好。Astro 生态里Starlight 内置了 Pagefind 搜索构建时生成一个静态搜索索引完全本地化速度很快。我迁移的站点采用的是 Pagefind 方案。Pagefind 的使用非常简单构建完成后执行一次索引生成命令在页面里引入它生成的脚本。为了让中文搜索也能良好工作需要指定语言参数否则默认按英文分词中文搜索时会出现令人困惑的结果。这里顺带提醒Pagefind 的索引文件会额外产生几十 KB 的静态资源如果在意体积可以考虑只在文档页面引入搜索脚本首页不加载。5.5 定制化样式覆盖的替代做法Docusaurus 的样式体系基于 CSS Modules 和 CSS 变量覆盖样式通常会走src/css/custom.css里的 CSS 变量重写。Astro 完全不做任何样式框架限制你可以用普通 CSS、CSS Modules、TailwindCSS 或 CSS-in-JS 方案。我迁移后直接使用现代 CSS 原生特性比如:has()选择器和嵌套 CSS配合 CSS 变量定义主题颜色。覆盖 Docusaurus 样式时我用[data-themelight]和[data-themedark]选择器分别定义颜色变量。Astro 里我用同样的方式在html标签上切换>

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

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

免费获取报价 →
↑