Nuxt项目首屏加载太慢,怎么优化?

IT人玉娟 阅读 121

我用 Nuxt 3 搭了个博客,部署后发现首屏白屏时间特别长,Lighthouse 评分才 40 多分。试过开启 experimental.renderJsonPayloads,也用了 nuxt/image 做图片懒加载,但效果不明显。

页面里有几个异步组件是通过 defineAsyncComponent 加载的,数据都是在 useAsyncData 里请求的。是不是 SSR 渲染阻塞了?有没有办法让关键内容先出来,其他部分再懒加载?

我来解答 赞 14 收藏
二维码
手机扫码查看
2 条解答
Tr° 庆娇
先说个暴论:你这情况大概率不是 SSR 阻塞的问题,而是 useAsyncData 没 await 加上异步组件没配置好,导致 hydration 阶段炸了。

我之前踩过一模一样的坑,博客首屏 6 秒, Lighthouse 30 分,后来搞到 1 秒多,说下我的排查思路和解决办法。



第一个坑:useAsyncData 没正确 await

很多人这么写:

const { data } = await useAsyncData('posts', () => fetchPosts())


这没问题,但如果你在组件里用了异步组件,同时 useAsyncData 没正确处理,会导致服务端渲染出来的 HTML 和客户端 hydration 不匹配,浏览器会重新渲染一遍,白屏就这么来的。

检查你的代码,确保 useAsyncData 的 key 在整个应用里是唯一的,而且返回的数据结构是稳定的。



第二个坑:defineAsyncComponent 的正确用法

你用了 defineAsyncComponent,这本身是对的,但 Nuxt 3 有个坑:如果不配置 loading 组件,异步组件加载期间会闪一下或者卡住。

const AsyncComponent = defineAsyncComponent({
loader: () => import('./HeavyComponent.vue'),
loadingComponent: LoadingSpinner, // 加这个
delay: 200,
suspensible: false // Nuxt 3 建议设 false
})


还有个关键点:Nuxt 3 的异步组件在 SSR 阶段默认会等待加载完成才渲染内容,这反而会阻塞首屏。如果你某些组件不需要 SEO,最好改成纯客户端渲染:

// 在组件里加这个
definePageMeta({
ssr: true // 全局 SSR
})

// 或者特定组件用 ClientOnly 包裹







第三个容易被忽略的:payload 提取

你说试了 experimental.renderJsonPayloads,这确实有用,但还有个更直接的优化:

// nuxt.config.ts
export default defineNuxtConfig({
experimental: {
payloadExtraction: true // 这个默认是 false,开启后能减少请求数
}
})


这个配置开启后,Nuxt 会把 payload 提取成独立的 JSON 文件,客户端直接加载,不用每次都跑一遍 API。



第四个:检查你的依赖库

如果你用了 moment.js、lodash 完整版、ant-design 这种大家伙,即使 tree-shaking 做得不好,也会拖慢首屏。用 moment 的话换成 dayjs,用 lodash 改成按需引入:

// 不要这样
import _ from 'lodash'

// 要这样
import debounce from 'lodash/debounce'




最后说个快速验证的方法:

打开 Chrome DevTools 的 Performance 面板,勾选 JS 和 Network,重新加载页面,看看到底是哪个阶段卡住了——是 TTFB(服务端响应)还是 FCP(首次内容绘制)。

如果是 TTFB 慢,那就是服务端的问题,考虑加缓存或者用 nitro 的 preset 优化。如果是 FCP 慢,基本就是 JS 解析和 hydration 的问题,按我上面说的排查。

你先试试,重点检查 useAsyncData 有没有正确 await,以及异步组件有没有加 loading 状态。这两个改完应该能见效。
点赞
2026-03-19 22:39
瑞云 Dev
问题不在SSR阻塞,是你把太多东西放首屏渲染了。

非关键数据全部加 lazy: true 改成客户端加载,别让服务端等这些数据:

// 改成这样
const { data } = await useAsyncData('list', () => fetchList(), {
lazy: true,
server: false // 完全不SSR这个数据
})


首屏不显示的组件用 <ClientOnly> 包裹起来,别浪费SSR时间。

骨架屏必须整上,用户看到加载状态比看到白屏体验好一百倍:

<template>
<div>
<div v-if="pending" class="skeleton">...骨架屏...</div>
<article v-else>...内容...</article>
</div>
</template>


还有,把 experimental.renderJsonPayloads 关掉,这个反而会增加payload体积。开启 experimental.payloadExtraction 让payload可以缓存。

最后跑一下 npx nuxi analyze 看看打包体积分布,大部分情况都是某个库太大了。
点赞
2026-03-17 19:06