卖坚果的怪叔叔 卖坚果的怪叔叔
  • 首页
  • 生活
  • 技术
  • 记录
  • 工具箱
  • 关于我
首页 › 技术 › Nuxt3中的水合是什么?以及使用中的一些总结!

Nuxt3中的水合是什么?以及使用中的一些总结!

坚果大叔
2025-08-30 19:42:35技术阅读 4,417

前提

项目前期的时候虽然在nuxt中开启了SSR,但是因为api请求还是用的客户端请求。所以并没有完全达到服务端渲染的目的,最主要的表现就是在源代码中没有体现动态数据的展示。

问题点

其实整个项目最主要解决的页面只有三个。首页、商品列表、商品详情页。但是这里面有个很坑的问题?

首页还好,所有的数据都是从接口获取的,直接将接口修改请求方式即可。但是列表和详情页的数据有些需要依赖本地数据。就是需要从接口获取当前的数据,还需要从之前预先请求的本地数据中获取特定的字段来合并最终的展示。

实践操作

在首页中直接采用useAsyncData的方式来进行数据请求。

const { data: goodsTabData } = await useAsyncData("goodsTabData", () =>
  IndexApi.fetchHomeGoodsTab(),
);

对于一些客户端数据请求,还在onMounted中进行处理。

onMounted(async () => {
  await reportActive();
});

而动态数据请求则通过watchEffect来处理api返回的数据。

watchEffect(() => {
  if (goodsTabData.value?.code === 0) {
    goodsTabs.value = goodsTabData.value.data.gameInfos;
    const gid = goodsTabData.value.data.gameInfos[0].gid;
    activeGoodsTab.value = gid;
    if (process.client) {
      getGoodsListData(gid);
    }
  }
});

需要注意的是,有些方法需要区分客户端还是服务端请求。通过process.client来进行判断。

在商品列表页面的时候,因为需要有客户端的操作,所以使用使用 useLazyAsyncData 来实现商品列表在服务端数据预取。同时区分客户端的操作,保持服务端和客户端水合一致。


const { data: initialGoodsData } = await useLazyAsyncData(
  goods-list-${route.params.gid},
  async () => {
    const gid = route.params.gid as string;
    if (!gid) return null;
    try {
      const res = await GoodsApi.xxxx({
        pageNum: 1,
        cat: "H",
      });

      if (res?.code === 0 && res?.data) {
        return {
          total: res.data.total,
          rows: res.data.rows || [],
        };
      }
    } catch (error) {
      console.error("服务端获取商品列表失败:", error);
    }

    return null;
  },
);

详情页也是,数据获取的层级太深,最终渲染的结果不仅依赖具体的详情接口返回的数据。返回数据的具体label名称。展示形式,展示交互全部依赖于前置请求的本地数据。

打个比方接口返回一个字段K:2,你需要根据当前商品的类型去特定的数据中找到K对应的名称是什么。然后展现最终的效果。

所以在详情页也是深度的依赖水合效果。html服务端渲染返回,但是也是数据转换操作在客户端处理。


const {
  data: goodsDetailsResponse,
  error: goodsDetailsError,
  pending: detailsLoading,
  refresh,
} = await useAsyncData(
    goods-detail-${goodsProductId.value},
  async () => {
    return response;
  },
  {
    server: true,
    default: () => ({ code: -1, data: null, msg: "" }),
  },
);

另外进入页面的初始交互也从onMounted改成通过watchEffect来处理。

  • 还有一个注意点,会发现在请求的时候有 goods-detail-${goodsProductId.value},有这么一个key。那么他是干嘛的呢?

他是useAsyncData 的唯一key。可以用来标识唯一的数据请求,当多个组件请求相同数据时可以通过这个key来区分是否重复请求。还可以自动处理依赖关系,比如goodsProductId发生变化的时候,会自动触发重新请求。

最终的效果

最终的效果就是在查看页面源代码的时候,相应的接口返回数据会在源代码中呈现。这样搜索引擎的爬虫才会抓取到页面的内容有更好的seo效果。

Nuxt3中的水合是什么?以及使用中的一些总结!-卖坚果的怪叔叔

Nuxt3中的水合是什么?以及使用中的一些总结!-卖坚果的怪叔叔

为什么要使用nuxt?因为vue是一个单页面应用,并且所有的内容都是客户端动态渲染的。这就导致无法在浏览器中查看源码的时候看到返回的数据内容。页面上只有固定的骨架内容。

在nuxt3中其实有多个数据获取方式,像上面所使用的api基本都是fetch封装的请求方法。所以在使用useAsyncData的时候需要手动开启ssr特性。

不同api的简单对比

useFetch vs $fetch


// useFetch - 响应式数据获取,支持 SSR
const { data, pending, error } = await useFetch('/api/goods')

// $fetch - 简单的 HTTP 请求,类似 fetch API
const data = await $fetch('/api/goods')
useAsyncData vs useLazyAsyncData

// useAsyncData - 阻塞式数据获取-等待数据加载完成后才渲染页面
const { data } = await useAsyncData('goods', () => $fetch('/api/goods'))

// useLazyAsyncData - 非阻塞式数据获取-立即渲染页面,数据异步加载
const { data, pending } = await useLazyAsyncData('goods', () => $fetch('/api/goods'))

他们的核心区别:

场景 推荐方法 原因
关键首屏数据 useAsyncData 确保数据完整性
次要数据 useLazyAsyncData 提升页面响应速度
用户交互数据 $fetch 简单直接
响应式数据 useFetch 自动状态管理

最后

另外在上文中的列表页面和详情页多次提高水合这个概念,那么什么是水合呢?他又解决了什么?

什么是水合?

水合就是将服务端渲染的静态HTML转换为可交互操作的客户端应用的过程。在这个过程中vue"接管"服务端生成的静态 HTML,为其添加事件监听器、数据绑定和其他客户端功能。

水合的过程

Nuxt3中的水合是什么?以及使用中的一些总结!-卖坚果的怪叔叔

  1. 服务端:生成静态 HTML 并发送给客户端
  2. 客户端:下载并执行 JavaScript
  3. 激活:Vue 应用接管 DOM,绑定事件监听器
  4. 同步:确保客户端状态与服务端渲染的内容一致

举一个简单的例子

const title = '水合示例' const count = ref(0)

服务端会生成<h1>水合示例</h1><button>点击次数: 0</button> 这个HTML并返回给客户端,而在客户端中vue会找到这个按钮并添加点击事件监听器。

另外在水合过程中如何找到对应的HTML呢。其实vue 项目在渲染的时候dom上都会data-v开头的标记。一个是通过这个来匹配。还有就是我们的虚拟dom树的比对。类似diff算法的更新的机制。

为了避免和解决水合不匹配的问题,我们可以采用一开始的方法。总结如下:

1、使用useAsyncData和useFetch确保数据一致性

2、使用process.client条件判断隔离客户端特定代码

3、使用ClientOnly组件包装仅客户端渲染的内容

4、确保服务端和客户端的初始状态一致

赞赏 赞(1)
Element Plus Upload 添加支持拖拽排序~
上一篇
使用uview-plus遇到的一些问题!
下一篇
在小程序中查看

这是个😊,挣点服务器费用😜

评论已关闭

热门文章
初冬!暖日!东太湖慢行!
ES6入门之let、cont
随便转转!
胜利的曙光,就要来了
近期
Python获取好友地区分布及好友性别分布
专题标签
agent (1) CSS (15) dom (1) Es6入门系列 (14) Git (3) html (1) iview (1) JavaScript (9) js (4) Lif (1) Node (4) pyecharts (1) Python (10) ref (1) scriptable (1) Skill (1) vsCode (1) vue (4) wordpress (2) 三月 (1) 上海 (1) 假期 (1) 其他分享 (3) 前端两三问 (15) 古镇 (1) 外滩 (1) 对象 (1) 小程序 (5) 年度总结 (6) 年终总结 (1) 微信机器人 (4) 打字机 (1) 技术 (1) 日常问题 (8) 日常问题记录 (1) 春天 (2) 服务器 (2) 海族馆 (1) 生活 (8) 网上国网 (1) 记录 (3) 通勤 (1) 重学JavaScript (11) 面试题 (10) 🌸 (1)
什么是Agent?
2026-08-15 23:49:12
329 0 1
什么是 Skill?
2026-08-12 21:32:42
290 1 0
失业在家,我给小朋友做了一个练字小程序!
2026-08-07 16:21:52
531 8 1
上线8年,15万用户的小程序,一共赚了多少钱?
2026-05-30 18:01:24
4,621 4 0
1
  • 1
博主

一位佛系的前端开发者,略通摄影,乐于尝试新事物,热衷于美食。

友链
故事胶片
公众号
坚果大叔 执行上下文 卖坚果的怪叔叔 Dacking
Copyright © 2017-2026 卖坚果的怪叔叔

开往-友链接力

萌ICP备20230818号

苏ICP备18048410号-3
  • 首页
  • 生活
  • 技术
  • 记录
  • 工具箱
  • 关于我
# CSS # # JavaScript # # vue # # 微信 # # 生活 #
坚果大叔
345
文章
170
评论
431
喜欢