You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Nuxt3中实现SSR友好的带全局状态的搜索Composable

Nuxt3 SSR友好的全局搜索状态实现方案

核心思路

通过Nuxt3的useState实现搜索关键词、结果、加载状态、错误信息的全局共享,同时封装统一的搜索执行方法,确保多组件间状态同步且兼容SSR渲染。

完整的useSearch Composable实现

// composables/useSearch.js
export default () => {
  // 全局共享状态:用唯一key标识,确保SSR时状态同步
  const toSearch = useState('search_query', () => 'John')
  const searchResults = useState('search_results', () => null)
  const isLoading = useState('search_loading', () => false)
  const searchError = useState('search_error', () => null)

  // 统一搜索执行逻辑
  const executeSearch = async () => {
    // 空关键词处理
    if (!toSearch.value.trim()) {
      searchResults.value = null
      searchError.value = null
      return
    }

    // 更新状态:开始加载
    isLoading.value = true
    searchError.value = null

    try {
      // 用encodeURIComponent处理搜索词,避免URL编码问题
      const response = await $fetch(`https://dummyjson.com/users/search?q=${encodeURIComponent(toSearch.value)}`)
      searchResults.value = response
    } catch (err) {
      searchError.value = err
      searchResults.value = null
    } finally {
      // 无论成功失败,结束加载状态
      isLoading.value = false
    }
  }

  return {
    toSearch,
    searchResults,
    isLoading,
    searchError,
    executeSearch
  }
}

组件使用示例

搜索输入组件(任意页面/组件)

<template>
  <div class="search-bar">
    <input
      v-model="toSearch"
      placeholder="输入关键词搜索用户"
      type="text"
    />
    <button
      @click="executeSearch"
      :disabled="isLoading"
    >
      {{ isLoading ? '搜索中...' : '搜索' }}
    </button>
  </div>
</template>

<script setup>
const { toSearch, isLoading, executeSearch } = useSearch()
</script>

结果展示组件(任意页面/组件)

<template>
  <div class="search-results">
    <div v-if="isLoading" class="loading">加载中...</div>
    <div v-else-if="searchError" class="error">
      搜索失败:{{ searchError.message }}
    </div>
    <div v-else-if="searchResults">
      <h3>找到 {{ searchResults.users.length }} 个用户</h3>
      <ul>
        <li v-for="user in searchResults.users" :key="user.id">
          {{ user.firstName }} {{ user.lastName }}
        </li>
      </ul>
    </div>
    <div v-else class="empty">请输入关键词进行搜索</div>
  </div>
</template>

<script setup>
const { searchResults, isLoading, searchError } = useSearch()
</script>

关键细节说明

  1. SSR友好性:所有状态通过useState定义,Nuxt会自动处理服务端与客户端的状态序列化与同步,避免 hydration mismatch 问题。
  2. 全局状态共享:useState的key(如search_query)是全局唯一标识,所有组件调用useSearch都会复用同一个状态实例。
  3. 防重复提交:通过isLoading状态控制按钮禁用,避免用户重复点击触发多次请求。
  4. 错误处理:统一捕获请求错误并同步到全局状态,方便任意组件展示错误信息。
  5. URL编码安全:使用encodeURIComponent处理搜索词,避免特殊字符导致的请求异常。

可选优化:结合useAsyncData缓存

如果需要复用相同关键词的搜索结果,可以将请求逻辑替换为useAsyncData,利用其缓存机制:

const executeSearch = async () => {
  if (!toSearch.value.trim()) {
    searchResults.value = null
    searchError.value = null
    return
  }

  const { data, pending, error } = await useAsyncData(
    // 用搜索词生成唯一key,实现不同关键词的独立缓存
    `search_${toSearch.value}`,
    () => $fetch(`https://dummyjson.com/users/search?q=${encodeURIComponent(toSearch.value)}`),
    {
      immediate: false, // 手动触发请求
      retry: 1 // 请求失败自动重试1次
    }
  )

  // 同步useAsyncData的状态到全局useState
  isLoading.value = pending.value
  searchError.value = error.value
  searchResults.value = data.value
}

内容的提问来源于stack exchange,提问作者Tim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 19:23:24