Nuxt 3 预加载与代码分割完整调优指南:3 处改动让页面切换肉眼可见地变快
【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt
对某 Nuxt 站点做 Lighthouse 审计,页面切换这一项只有 62 分;Performance 面板里,目标页面的 JS 在用户点击之后才开始下载——瓶颈不在服务器响应,而在“资源没到”。Nuxt 3 内置了智能预加载(prefetch):链接可见时提前下载目标页的 JS、布局与中间件资源。本文按一次真实调优的顺序展开:先量出差距,再逐项调整,最后划清边界,不要求你事先读过官方文档。
动手改配置之前,先弄清时间花在哪了
调优最怕凭感觉。三个工具各看一个数字:
| 要量什么 | 用什么 | 看什么 |
|---|---|---|
| 初始包里谁最重 | nuxi analyze | 可视化报告里最大的区块 |
| 交互到加载的时延 | Chrome DevTools 的 Performance 面板 | “点击时刻”与“目标页第一个资源请求”的间隔 |
| 综合得分 | Lighthouse 或 PageSpeed Insights | LCP、INP 和具体失败的审计项 |
在 Performance 面板里录制一次完整的页面切换,重点是比较上表第二行那个间隔。调优前实测(示例环境,M1 笔记本 + 模拟 4G)约为 900ms;调优后应稳定在 300ms 以内,最好接近 0。
# 生成生产包的可视化分析,最大区块通常就是第一个要处理的组件 npx nuxi analyze跑完打开报告,如果某个第三方库或大组件占了主包的一半以上,它大概率比任何预加载手段都值得先处理。
链接预加载只在链接可见时发生
页面切换卡顿最直接的抓手是<NuxtLink>内置的预加载——它把“用户点击”那一刻的等待,提前挪到“链接出现在屏幕上”的那一刻。
默认行为
prefetchOn默认是visibility:Nuxt 用 IntersectionObserver 监听链接进入视口,再在浏览器空闲时(requestIdleCallback)预下载目标页组件,同时预拉取该路由的布局与中间件。它自带两道保险——离线或 2G 慢网时跳过;并发的路由预加载最多 4 个,超出的排队。注意一点:给<NuxtLink>加了custom属性后,这些监听不会自动挂上,需要你在插槽里手动调用暴露出来的prefetch。
怎么调
按“带宽预算”选择触发时机,而不是按喜好:
| 配置 | 写在哪 | 效果 |
|---|---|---|
prefetchOn="interaction" | 单个链接 | 仅悬停/聚焦时预加载,省带宽 |
no-prefetch | 单个链接 | 该链接完全不预加载 |
experimental.defaults.nuxtLink | nuxt.config.ts | 全局改默认行为 |
export default defineNuxtConfig({ experimental: { defaults: { nuxtLink: { // 全站链接改为悬停才预加载:视口内出现不再触发 prefetchOn: { interaction: true, visibility: false }, }, }, }, })单页里只有个别链接想保留默认行为时,用属性覆盖配置即可。各属性的完整语义见 NuxtLink 组件文档。
如何确认生效
重新录制一次 Performance:悬停链接之前,瀑布流里不应出现目标页的 JS;悬停后应能看到对应的modulepreload请求。给链接配一个prefetchedClass,还能在页面上直接看到哪些链接“已就绪”。
LCP 图片要声明高优先级,而不是只靠懒加载
图片通常占页面重量的大头,而 LCP 只关心一件事:那张最大的首屏图多快开始下载。这和预加载解决的不是同一个问题——预加载管“下一个页面”,它管“当前页面的第一张图”。
默认行为
原生<img>没有优先级概念,首屏图和脚本、字体在带宽上排队竞争。loading="lazy"(原生懒加载)能推迟视口外图片,但对首屏那张图,浏览器默认不知道它最重要。
怎么调
安装 Nuxt Image 模块(npx nuxt module add image)后,首屏图显式声明高优先级:
<!-- 首屏大图::preload 会注入 <link rel="preload"> 并标记 fetchpriority="high" --> <NuxtImg src="/hero-banner.webp" width="800" height="300" loading="eager" :preload="{ fetchPriority: 'high' }" />视口内的其他图片保持loading="lazy"即可。别把所有图都标成高优先级——“高优先级”一旦泛滥,等于没有优先级。
如何确认生效
查看页面源码,确认存在指向该图片的<link rel="preload" as="image" fetchpriority="high">;再在 Performance 面板确认 LCP 元素的加载起始时间明显前移。如果你的 LCP 根本不是图片(比如一段大标题文字),这一项可以跳过。
不需要的代码不该进初始包
预加载优化的是“点击那一刻”,代码分割优化的是“页面第一次到达那一刻”,两者叠加才完整。
默认行为
Nuxt 默认按路由分包,但页面用到的组件仍会被打进该路由的 chunk。它提供了两个零配置的开关:Lazy前缀组件——真正渲染到之前不加载对应代码;hydrate-on-visible懒水合——首屏先输出静态 HTML,等组件滚入视口再执行它的 JavaScript(Nuxt 3.16+)。
怎么调
<!-- 重型列表:用户点开才加载代码,而不是页面到达就加载 --> <LazyProductList v-if="show" /> <!-- 首屏装饰组件:先显示静态内容,可见时再水合 --> <HeroCard hydrate-on-visible />如何确认生效
重新跑nuxi analyze,目标组件应从主 bundle 消失、变成独立 chunk;Network 面板里它的加载时机应在点击/可见之后。懒加载与懒水合的完整规则在 性能优化指南 里有专门章节。
预加载是双刃剑
预加载开满不等于更快:长页面里链接很多、用户大概率只点一个,默认的 visibility 预加载会在滚动时把一连串页面 JS 提前拉下来,和当前页的资源抢带宽——在弱网下表现为滚动掉帧、白屏概率上升。
| 场景 | 处理方式 | 代价 |
|---|---|---|
| 长列表页,链接点击率低 | 该组链接加no-prefetch | 失去“点开即达”的体验 |
| 低端移动设备占比高 | 全局改为prefetchOn: { interaction: true } | 悬停后到资源就绪略有延迟 |
| 内容型静态站,几乎无客户端交互 | 全局prefetch: false,配合预渲染 | 导航退化为完整页面加载 |
另外别重复造 Nuxt 已有的保险:慢网自动跳过、并发上限 4,这些内建逻辑在你只调参数时都会继续生效。真正该警惕的是叠加了第三方预加载插件或手写<link rel="preload">的项目——两套机制同时抢带宽时,省下的时间会抵消掉。
调完对着这份清单逐项打勾:
- DevTools 里“点击”到“目标页资源开始下载”的间隔已小于 300ms
- 预加载触发时机(visibility / interaction)是显式选的,不是默认碰巧
- 首屏 LCP 图带
preload且fetchpriority="high" - 重组件已用
Lazy前缀或hydrate-on-visible移出初始包,nuxi analyze可见独立 chunk - 低点击率的链接都加了
no-prefetch,没有两套预加载机制在互相抢带宽
【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考