首页 / Astro 教程 / 性能优化与 Prefetch

Astro 教程

性能优化与 Prefetch

本教程共 56 篇 · 第 46 篇 · 更新于 2026-08-07 · 约 11 分钟阅读

AstroAstro 教程性能优化Prefetch预取零 JS客户端指令群岛架构

本节目标:理解 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:visibleclient: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 才能在所有浏览器稳定生效。