SSR页面上的按钮点不了?Nuxt 懒加载水合揭秘💧

写在开头

Hello吖,各位UU们好!👏

今是2026年03月14日,下午,幽静、无人打扰,刚刷了会手机,但有点看腻了。

然后,今天上午,小编将自己的上一台电脑叫了一个转转来上门回收,2021年款,联想小新R7,本来在APP上预估是能卖两千二左右的,结果线下验机后说只能卖1700了,就没卖,想着再找一个爱回收看看价格,🤔不知道能不能涨点。

还有个事,昨天听朋友说,他网恋成功了,说是在Soul上找的对象,已经线下面基过。唉,这年头...这也能成功?🥶 你们说小编要不要也去试试?🤔

好了,回到正题,今天要来分享的是小编上周工作中排查的一个问题,其实也是比较基础的概念问题,只是小编太久没用了,这次也写出来记录一下,请诸君按需食用哈~

需求背景 💡

最近小编正在做一个 SSR 项目,作为一名 Vue 老玩家,自然就选择 Nuxt 来搞,上次用 Nuxt 还是在上次,时间略久了!😗

整体项目开发进展还算顺利,也就是部署稍微麻烦一丢丢。然而,这天测试同学给我提了个问题:

"页面加载出来后,有时按钮点了没啥反应,总要多点几次或者要等一会才能点。"

小编一开始也按常规思路来:先看控制台有没有报错 ------ 结果没有明显的红字错误(因为并不是水合错位报错,只是水合还没执行到那块,事件还没绑上)。于是怀疑是事件没绑好或者代码写错了,又查了一圈事件和逻辑,代码确实没问题!🤔

最后才反应过来:原来是 水合(Hydration) 还没完成,那部分组件还没绑上事件,所以有时候才能点。

什么是水合?

上面说了,按钮点不了是因为水合还没完成。那水合到底是什么?🤔

简单说:服务端先返回 HTML,客户端 JS 加载完后,把事件绑上去,让页面能点、能交互------这个过程就叫水合

下面简单用 CSR 和 SSR 对比一下,帮你建立直觉。

传统 CSR(客户端渲染)

普通的 Vue SPA 应用是这样的:

js 复制代码
用户访问页面
  ↓
加载空白 HTML + JS
  ↓
JS 执行,渲染页面
  ↓
用户看到内容,可以交互 

缺点:首屏白屏时间长,SEO 也不友好。

SSR(服务端渲染)

SSR 是这样的:

js 复制代码
用户访问页面
  ↓
服务端直接返回完整 HTML
  ↓
用户立刻看到内容(快!)
  ↓
加载 JS,执行"水合"
  ↓
页面变得可交互

优点:首屏快,SEO 友好。

很明显,CSR 和 SSR 是两种不同的取舍,没有谁一定更好,咱们得根据业务场景来选,不要一刀切。❌

问题来了

SSR 有个尴尬的地方:HTML 先出来了,但 JS 还没加载完,事件还没绑定上

js 复制代码
用户看到页面了
  ↓
想点按钮 → 点不了 ❌(JS 还没准备好)
  ↓
等 1-2 秒...
  ↓
终于能点了

这就是测试同学遇到的问题!页面出来了,但还处于"僵尸"状态,看得见摸不着。😅

懒加载水合是什么?

既然问题是「要等一会儿才能点」,那有没有办法让首屏更快可交互?小编查了一下 Nuxt 的文档,发现有个功能叫 懒加载水合(Lazy Hydration),专门解决这类问题!

懒加载水合 :它还是「水合」------还是把事件绑到服务端 HTML 上,只是不再一次性水合整页,而是按需、分优先级地水合。如,首屏先水合,下面的等需要时再水合。

所以呢,用词上要分清:水合 是整个过程,懒加载水合 是水合的一种策略(延迟一部分组件的水合时机)。

在 Vue 3.5 / Nuxt 里,这个策略常和 异步组件 一起用:异步组件负责延迟加载 组件 JS(减包体),懒加载水合负责延迟 该组件的水合时机(让首屏先可交互),两个搭配着用。

核心思想:不用一次性把所有组件都水合,按需水合!

比如:

  • 首屏可见的组件 → 立刻水合
  • 非首屏的组件 → 用户滚到那里再水合
  • 低优先级的组件 → 浏览器空闲时再水合

这样,首屏的 JS 体积就小了,水合速度就快了,用户点按钮就不会"卡壳"啦!🎯

Nuxt 中怎么用?

Nuxt 已经内置了懒加载水合的支持,用起来非常简单的!🏃

第1️⃣步:认识 Lazy 组件

在 Nuxt 中,所有放在 components/ 目录下的组件都会被自动导入。如果在组件名前加上 Lazy 前缀,就可以延迟加载:

js 复制代码
<template>
  <!-- 普通组件 -->
  <MyComponent />

  <!-- 懒加载组件 -->
  <LazyMyComponent />
</template>

但这只是 懒加载 ,还不是 懒加载水合!区别在于:

  • 懒加载:延迟加载 JS 代码
  • 懒加载水合:延迟执行水合(JS 可能已经加载了,但不急着绑定事件)

第2️⃣步:添加水合策略

Nuxt 提供了多种水合策略,咱们来看几个常用的:

hydrate-on-visible(可见时水合)

组件进入视口时才水合,适合非首屏内容:

js 复制代码
<template>
  <div>
    <h1>首屏内容</h1>

    <!-- 下面的组件要用户滚到这里才会水合 -->
    <LazyComments hydrate-on-visible />
  </div>
</template>

🍊 为什么这么做❓

非首屏的组件,用户不一定马上会看到,何必急着水合呢?等用户滚到那里再说,这样首屏更快。

hydrate-on-interaction(交互时水合)

用户点击/悬停组件时才水合:

js 复制代码
<template>
  <!-- 用户点击这个区域时才水合 -->
  <LazyExpensiveComponent hydrate-on-interaction="click" />

  <!-- 或者鼠标悬停时水合 -->
  <LazyChart hydrate-on-interaction="mouseover" />
</template>

hydrate-after(延迟水合)

指定毫秒数后自动水合:

js 复制代码
<template>
  <!-- 2 秒后水合 -->
  <LazySidebar :hydrate-after="2000" />
</template>

hydrate-on-media-query(媒体查询水合)

匹配特定媒体查询时水合:

js 复制代码
<template>
  <!-- 只在移动端水合 -->
  <LazyMobileMenu hydrate-on-media-query="(max-width: 768px)" />
</template>

hydrate-when(条件水合)

根据条件决定是否水合:

js 复制代码
<script setup>
const isReady = ref(false)

// 某个条件触发后
function triggerHydration() {
  isReady.value = true
}
</script>

<template>
  <LazyHeavyComponent :hydrate-when="isReady" />
</template>

第3️⃣步:监听水合完成事件

所有懒加载水合组件都会触发 @hydrated 事件:

js 复制代码
<template>
  <LazyComments
    hydrate-on-visible
    @hydrated="onHydrated"
  />
</template>

<script setup>
function onHydrated() {
  console.log('组件水合完成!')
}
</script>

第4️⃣步:小编的实际应用

回到咱们的场景,测试反馈按钮点不了,小编的解决方案是这样的:

js 复制代码
<template>
  <div>
    <!-- 首屏重要内容,正常水合 -->
    <Header />
    <MainContent />

    <!-- 非首屏的评论区,懒加载水合 -->
    <LazyComments hydrate-on-visible />

    <!-- 底部推荐,用户悬停时才水合 -->
    <LazyRecommendations hydrate-on-interaction="mouseover" />
  </div>
</template>

这样首屏的 JS 体积就小了,水合速度变快,按钮响应更及时!🎉

💡 小贴士

  • 首屏核心交互内容不要用懒加载水合,会影响用户体验。
  • 适合用在非首屏、低优先级的组件上。
  • 如果组件本身就用了 v-if="false",那就不需要懒加载水合了。

Vue 3.5 原生用法

如果你用的不是 Nuxt,而是纯 Vue 3.5 + 自己搭的 SSR,其实也可以用原生的懒加载水合。

底层原理其实是 Vue 3.5 提供的水合策略,Nuxt 只是在上面封装了一层更易用的 API。

官方文档:传送门

第1️⃣步:导入水合策略

js 复制代码
import { defineAsyncComponent, hydrateOnVisible } from 'vue'

第2️⃣步:定义异步组件

js 复制代码
const LazyComments = defineAsyncComponent({
  loader: () => import('./Comments.vue'),
  hydrate: hydrateOnVisible()
})

可用的水合策略

策略 说明
hydrateOnIdle() 浏览器空闲时水合
hydrateOnVisible() 进入视口时水合
hydrateOnInteraction('click') 点击时水合
hydrateOnMediaQuery('(max-width:768px)') 媒体查询匹配时水合

用法都差不多,小编就不一一列举了,大家看文档就好~😋


至此,本篇文章就写完啦,撒花撒花。

希望本文对你有所帮助,如有任何疑问,期待你的留言哦。

老样子,点赞+评论=你会了,收藏=你精通了。

相关推荐
PursuitofHappiness1 小时前
2 tree-cli 的使用方法
前端
不做超级小白2 小时前
把图片压小,但不糊:reduceUrImgs项目关键点拆解
前端·开源·node.js
耀耀切克闹灬2 小时前
Eruda 移动端调试工具使用指南
前端
王二端茶倒水2 小时前
现在AI Agent 已经能够代替程序员的工作了,作为一个程序员的我该如何规划以后的职业,请认真思考后给我最靠谱可行的建议。
前端·后端·面试
CyrusCJA2 小时前
毛玻璃效果
前端·css·css3
光影少年2 小时前
Monorepo架构是什么,如何学习Monorepo架构?
前端·学习·架构·前端框架
yuandiv2 小时前
让 Playwright 测试管理更优雅的利器
前端
拉拉肥_King2 小时前
Ant Design Vue a-image 图片预览充满全屏?为啥?
前端
OpenTiny社区2 小时前
生成式UI,AI交互的下一个十年?OpenTiny在QCon 2026的深度分享
前端·开源·github