性能优化与 Prefetch
本教程共 56 篇 · 第 46 篇 · 更新于 2026-08-07 · 约 11 分钟阅读
本节目标:理解 Astro 为什么天生快,并学会用内置的 prefetch(预取)功能让站内跳转接近瞬开,同时掌握与客户端指令配合、优化构建产物的一般思路。
网站快不快,直接影响访客会不会留下来。这一章我们不堆术语,先把「Astro 为什么快」讲清楚,再讲一个几乎零成本就能让体验飞升的功能:prefetch(预取)。
Astro 为什么天生就快
普通的前端框架,会把整页都打包成一段 JavaScript,浏览器要先下载、再执行,最后才把页面「画」出来。页面越大,这段脚本越重,访客等待就越久。
Astro 走的是另一条路。它默认只往浏览器发送纯粹的 HTML 和 CSS,不发送任何 JavaScript。页面在构建时就渲染成了静态文件,浏览器拿到就能直接显示。这就是官方说的「默认零 JS」(zero JS by default)。
这种思路在 Astro 里有个核心概念,叫群岛架构。整页绝大部分是静态 HTML,只有你明确标出来的交互组件(也就是岛屿)才会加载脚本。岛屿之外的地方,根本不需要 JS。
对性能的好处很明显:首屏渲染快、可交互时间早、移动端流量省。你写的页面越多,这个优势越明显,因为每个页面默认都是「零负担」的。
预取 Prefetch 解决什么问题
虽然 Astro 页面本身很轻,但当你点一个站内链接跳到下一页时,浏览器还是要重新去请求那个页面。网络有延迟,于是「点击 → 看到新页面」之间还是有空隙。
prefetch(预取)做的就是这件事:它在你真正点之前,偷偷把目标页面先下载到浏览器缓存里。等你一点,页面几乎瞬间出现,体验接近单页应用(SPA),但你的网站本质还是多页应用(MPA),保留了 Astro 的所有性能优势。
「预取」英文是 prefetch,意思是在后台提前抓取资源。Astro 7 里它是框架内置能力,不需要再装 @astrojs/prefetch(那个老集成早在 v3.5 就废弃了,别再用)。
三步开启预取
开启 prefetch 只需要改配置,再给链接加一个属性。
第一步,在 astro.config.mjs 里打开它:
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
prefetch: true
});
加上这一行后,Astro 会自动给全站每个页面注入一段预取脚本。但此时它还没生效——你还得明确告诉它「哪些链接要预取」。
第二步,在你想预取的 <a> 链接上加 data-astro-prefetch 属性:
<a href="/about" data-astro-prefetch>关于我们</a>
注意,预取只对站内的链接有效,指向外部网站的链接不会生效,这是出于安全和带宽的考虑。
就这么简单。当访客把鼠标悬停(或键盘聚焦)到这个链接上时,浏览器会在后台把 /about 页面抓下来。下一次点击就是「秒开」。
四种预取策略
Astro 支持四种预取策略,对应不同场景。你不写策略时,默认是 hover。
hover(默认):鼠标悬停或键盘聚焦时预取。最省流量,适合大多数链接。tap:在你点击的前一刻才预取。比 hover 更克制。viewport:链接一进入浏览器可视区域就预取。适合首屏下方会看到的链接。load:页面加载完成后,预取页面上所有链接。最激进,适合小站或重要内容页。
指定策略时,把值写在属性里即可:
<a href="/about" data-astro-prefetch="tap">关于我们</a>
<a href="/pricing" data-astro-prefetch="viewport">价格</a>
Astro 内部做了带宽保护:如果访客开了「省流量模式」或网络很慢,预取会自动降级成 tap,并且快速划过的链接不会被预取。你基本不用担心它乱费流量。
全站默认预取与退出
如果你希望全站所有链接都自动预取,不用一个个加属性,可以把配置改成:
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
prefetch: {
prefetchAll: true
}
});
开启 prefetchAll 后,即使链接没有 data-astro-prefetch 属性也会被预取。想对个别链接「例外」关掉,加 false 就行:
<a href="/huge-report" data-astro-prefetch="false">大文件报告</a>
你还可以改全局默认策略。比如让所有默认链接用 viewport:
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
prefetch: {
defaultStrategy: 'viewport'
}
});
在脚本里手动预取
有些跳转不是 <a> 链接,比如一个按钮触发跳转。这种情况可以用 astro:prefetch 模块里的 prefetch() 函数手动触发:
<button id="btn">点我跳走</button>
<script>
import { prefetch } from 'astro:prefetch';
const btn = document.getElementById('btn');
btn?.addEventListener('click', () => {
prefetch('/about');
});
</script>
这个函数在「省流量模式」和「慢网络」下同样会自动克制。如果你想强制预取(比如确信访客一定点),传 { ignoreSlowConnection: true }:
prefetch('/about', { ignoreSlowConnection: true });
提醒一句:prefetch() 依赖浏览器 API,只能写在客户端 <script> 里,别放到组件顶层的 frontmatter 中。
与客户端指令的配合
prefetch 预取的是「页面 HTML」,而岛屿的水合脚本是另一回事。当你在一个岛屿上用了客户端指令(比如 client:visible、client:load),预取来的页面里这些脚本并不会自动提前执行——它们仍按各自的指令时机去水合。
也就是说,prefetch 和部分水合是两层优化:prefetch 让「页面骨架」先到,客户端指令决定「交互脚本何时醒来」。两者配合,访客既能秒开页面,又不会为用不到的交互提前付 JS 成本。
如果你的站点开了视图过渡(ClientRouter),prefetch 会默认开启,且等价于 { prefetchAll: true },全站链接都预取。你仍可像上面那样在配置里覆盖它。
构建产物与资源优化思路
除了预取,Astro 在构建阶段就帮你做了不少优化,了解这些能帮你判断「还能在哪省」:
- 图片:第 23 章讲过的
astro:assets,构建时会自动压缩、转格式(WebP/AVIF)、按容器尺寸生成合适大小的图。这是减体积的大头,记得用<Image />而不是裸<img>。 - CSS:组件样式默认作用域隔离,且只打包用到的部分,死代码会被摇树(tree-shaking)掉。
- JS:因为只有岛屿才带脚本,整体 JS 体积天然很小。尽量把交互限制在必要组件上,别把整页都变成岛屿。
- 预取脚本本身:那段注入的 prefetch 脚本很小,且与上面的优化互不冲突。
一句话:Astro 的性能优化是「默认就快 + 按需加料」。先享受零 JS 的红利,再用 prefetch 补上跳转的体验短板,一般站点做到这两步就够用了。
浏览器兼容与缓存头
Astro 预取优先用浏览器原生的 <link rel="prefetch">,不支持时自动回退到 fetch()。
这里有个实战要点(虽是知识点,但很重要):预取后的页面能否被复用,取决于目标页面的缓存头。静态页或预渲染页,部署平台通常会自动带上 ETag,一般开箱即用;动态或 SSR 渲染的页面,建议自己设置 Cache-Control 等响应头,否则在 Safari 等浏览器上预取可能失效。
所以部署时(第 50、51 章会展开),确认平台给你页面配了合理的缓存头,prefetch 才能在所有浏览器稳定生效。