☰
Nuxt 资源预加载提速实战:让页面在用户点击前就位
2026/10/26 13:30:42 网站建设 项目流程

Nuxt 资源预加载提速实战:让页面在用户点击前就位

【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt

用 Nuxt 做项目一段时间后,总躲不过三个场景:移动端首屏要等好几秒,点击导航切路由时闪一下白屏,首页大图把 LCP 拖得很难看。这些问题的共性不是"资源太大",而是"资源到得太晚"。Nuxt 内置的预加载体系正好解决它——<NuxtLink>自动预取、按路由的代码分割、Lazy前缀延迟加载、懒水合(lazy hydration)、<NuxtImg>图片优先级加载。思路很简单:在用户需要之前把资源备齐,同时别把带宽浪费在没人点的链接上。

NuxtLink 默认就在预取,三种时机怎么选

打开 packages/nuxt/src/app/components/nuxt-link.ts 能看到,<NuxtLink>对内部链接默认开启预取:用 IntersectionObserver 盯着链接元素,一旦进入视口,就通过requestIdleCallback在浏览器空闲时拉取目标路由的组件代码,并在断网或 2G 弱网(isSlowConnection())下自动跳过。也就是说,你什么都不写,首屏附近的链接就已经在后台准备资源了。

想改变触发时机,用prefetchOn和no-prefetch两个属性:

<NuxtLink to="/about">关于我们</NuxtLink> <!-- 默认:进入视口即预取 --> <NuxtLink to="/pricing" prefetch-on="interaction">报价</NuxtLink> <!-- 悬停/聚焦才预取 --> <NuxtLink to="/legacy" no-prefetch>旧版入口</NuxtLink> <!-- 彻底关掉 -->

这段代码解决"什么时候开始下载"的问题:visibility是默认档,用户还没点,资源已经就位;interaction把成本压到最低,但点击后需要等一小会儿;no-prefetch则把带宽完全让给关键资源。

策略触发点适合的场景
visibility(默认)链接滚入视口,空闲时执行首屏导航、高概率被点击的入口
interaction鼠标悬停或键盘聚焦长列表、低点击率链接、流量敏感
no-prefetch不预取低版本入口、调试期、弱网兜底

时机只是单链接的局部决策;站点流量结构固定的话,更省事的做法是全局统一调一次。

在 nuxt.config.ts 里批量调 NuxtLink 的预取策略

单条链接上改属性太碎。experimental.defaults.nuxtLink可以一次性设定全站的预取行为,写法见 官方 NuxtLink 文档:

// nuxt.config.ts export default defineNuxtConfig({ experimental: { defaults: { nuxtLink: { prefetch: true, prefetchOn: { interaction: true, visibility: false }, }, }, }, })

这一段把全站链接从"见一面就预取"改成"要点了才预取",移动端流量开销能明显降下来,而用户真实要点击的那一次,预取依然来得及。如果后续想更激进,文档里还有experimental.crossOriginPrefetch(基于 Speculation Rules API)可以预取跨域路由。

预取的是哪个路由,取决于 Nuxt 的路由分割,这里有个常被忽视的并发上限。

路由级代码分割:预取的实际边界

每个页面文件在构建后对应独立的 JS chunk,<NuxtLink>的预取拉取的正是目标路由的这些组件代码——这也是为什么"预取"能换来到达速度。但 Nuxt 不会无脑并发下载:在 packages/nuxt/src/app/composables/preload.ts 的preloadRouteComponents里,同一时刻最多 4 个路由在预加载,超出的请求会先排队,等前面的解析完再继续。所以你在首页滚过几十个链接,也不会触发预取风暴。

页面级元信息可以写在definePageMeta里,布局、中间件、keepalive都在这里声明,参考 pages 文档。页面之外的组件,可以用更轻的两级手段削首屏体积:

<template> <LazyChartPanel hydrate-on-visible /> </template>

Lazy前缀让组件代码只在真正渲染时才动态导入(官方 性能文档 推荐的标准写法),hydrate-on-visible则进一步推迟水合,等组件滚进视口才变成可交互的。弹窗里的富文本编辑器、报表图表都该这么处理——它们可能几分钟才出现一次,却常被塞进首屏 bundle。

JS 体积降下来后,下一个大头通常是图。

首屏图片:NuxtImg 的优先级加载与 WebP 转换

<NuxtImg>是原生<img>的替代品:按width/height自动裁剪尺寸,转换 WebP/AVIF,支持响应式sizes和原生懒加载,需要先装 Image 模块(npx nuxt module add image),详见 NuxtImg 文档。图片提速的核心是给 LCP 图片"插队",让其余图片靠边站:

<NuxtImg src="/hero-banner.jpg" :preload="{ fetchPriority: 'high' }" loading="eager" format="webp" width="1200" height="400" />

preload会生成fetchpriority="high"的预加载声明,让这张图在 CSS、字体之前就开始下载——它直接决定 LCP。配合format="webp",同样内容的体积通常能再降 25%~35%。首屏以下的图片不要碰preload,一律交给loading="lazy"。

到这里,链接、组件、图片三条线都调完了,最后收掉几个最容易翻车的点。

预加载的三个坑,一次说清 ⚠️

  • custom插槽不自动挂预取逻辑:用v-slot自定义渲染时,prefetch、prefetchOn都失效,必须在pointerenter/focus上手动调用插槽返回的prefetch()(配合shouldPrefetch('interaction')判断),详见 NuxtLink 文档的 Custom Prefetch Triggers 一节。
  • 别把visibility和interaction同时打开:官方文档原话是 "That you probably don't want both enabled!",两个触发器会让同一资源在不同条件下被预取两次。
  • 外部链接和target="_blank"本来就不参与预取:代码里写死了,不用配;但如果你发现某个内部链接没被预取,先检查它是不是被no-prefetch或:prefetch="false"关掉了。

改完怎么验证?两件事就够:Chrome DevTools 的 Network 面板里,滚到链接附近时观察_nuxt下的 js 是否提前出现;再跑一次nuxi analyze,看 bundle 可视化里有没有异常的大块,有就按前面的Lazy前缀方案拆掉。

动手清单

  • 首屏导航保留默认visibility预取,长列表链接统一改成prefetch-on="interaction",低频入口加no-prefetch
  • 弹窗、图表类重组件加上Lazy前缀;纯展示型服务端组件加hydrate-on-visible
  • 给 LCP 图加:preload="{ fetchPriority: 'high' }"与format="webp",其余图片loading="lazy"

【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询