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

Next.js服务端数据获取时如何实现加载状态提示?

问题分析与解决方案

你的核心问题在于:Search是服务端组件(Server Component),生产环境下服务端会完成所有异步数据请求(getServerSession、getHotelAvail)后,才会把完整HTML发送给客户端,导致用户在等待期间看不到任何加载反馈。开发环境因为Next.js的调试优化,表现出的加载体验和生产环境不一致。

下面是具体的解决步骤和Next.js最佳实践:


第一步:修正冗余的表单提交逻辑

当前代码里的router.refresh、window.location.reload属于重复操作,会加重页面加载延迟,先简化提交逻辑:

else {
  let obj = { 
    ...values, 
    checkIn: format(date.from, "yyyy-MM-dd"), 
    checkOut: format(date.to, "yyyy-MM-dd"), 
    city: values.city.city, 
    lat: values.city.lat, 
    lng: values.city.lng 
  };
  
  if (values.city.state_province) obj.state = values.city.state_province;

  let x = getPairs(obj)
    .map(([[key0, ...keysRest], value]) =>
      `${key0}${keysRest.map(a => `[${a}]`).join('')}=${value}`)
    .join('&');
    
  // 仅保留这一行,router.push会自动触发服务端组件重新获取数据
  await router.push(`/search?${x}`);
}

第二步:用Suspense实现流式渲染(核心解决方案)

利用Next.js的Suspense组件实现流式渲染:让服务端先发送加载状态的HTML,等数据请求完成后再发送搜索结果的内容,用户就能即时看到加载反馈。

1. 重构Search页面结构

假设你的搜索页面是app/search/page.js,修改为以下结构:

// app/search/page.js
import { Suspense } from 'react';
import Search from './Search'; // 原服务端组件
import SearchLoader from './SearchLoader'; // 自定义加载组件

export default async function SearchPage({ searchParams }) {
  return (
    <div className="search-page">
      {/* Suspense会先渲染fallback的加载器,直到Search组件的异步操作完成 */}
      <Suspense fallback={<SearchLoader />}>
        <Search searchParams={searchParams} />
      </Suspense>
    </div>
  );
}

2. 创建客户端加载组件

SearchLoader是客户端组件,用来显示骨架屏或加载动画:

// app/search/SearchLoader.js
'use client';

export default function SearchLoader() {
  return (
    <div className="search-loader-container">
      {/* 示例骨架屏,可根据你的UI自定义 */}
      <div className="hotel-skeleton">
        <div className="skeleton-img"></div>
        <div className="skeleton-line"></div>
        <div className="skeleton-line short"></div>
      </div>
      <div className="hotel-skeleton"></div>
      <div className="hotel-skeleton"></div>
    </div>
  );
}

第三步:(可选)添加表单提交即时反馈

如果想让用户点击提交按钮的瞬间就看到加载状态,可在表单组件(客户端组件)中添加状态管理:

// 表单组件(需添加'use client'指令)
'use client';
import { useState } from 'react';
import { useRouter, usePathname } from 'next/navigation';
import { format } from 'date-fns';

export default function SearchForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const router = useRouter();
  const path = usePathname();
  // 假设values、date是你的表单状态
  const [values, setValues] = useState({});
  const [date, setDate] = useState({ from: new Date(), to: new Date() });

  const handleSubmit = async (e) => {
    e.preventDefault();
    setIsSubmitting(true);

    try {
      // 构建查询参数逻辑(和之前一致)
      let obj = { 
        ...values, 
        checkIn: format(date.from, "yyyy-MM-dd"), 
        checkOut: format(date.to, "yyyy-MM-dd"), 
        city: values.city.city, 
        lat: values.city.lat, 
        lng: values.city.lng 
      };
      if (values.city.state_province) obj.state = values.city.state_province;

      let x = Object.entries(obj)
        .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
        .join('&');
        
      await router.push(`/search?${x}`);
    } catch (err) {
      console.error('搜索失败:', err);
    } finally {
      setIsSubmitting(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* 表单字段 */}
      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? '搜索中...' : '开始搜索'}
      </button>
      {/* 可选:全局加载遮罩 */}
      {isSubmitting && <div className="loading-overlay"></div>}
    </form>
  );
}

Next.js 最佳实践

  1. 用Suspense处理服务端组件异步加载:这是App Router推荐的流式渲染方案,能让页面逐步加载,提升用户感知速度。
  2. 避免不必要的页面刷新:router.push已足够触发服务端组件重新获取数据,不要使用router.refresh或window.location.reload,这些操作会强制全页重渲染。
  3. 拆分组件粒度:把需要异步数据的部分拆成独立服务端组件,用Suspense包裹,让页面静态部分(导航、页脚)先渲染。
  4. 客户端组件处理交互状态:表单提交、按钮加载等用户交互逻辑放在客户端组件,服务端组件专注于数据获取和静态内容渲染。
  5. 生产环境测试:开发环境的调试优化会掩盖真实加载体验,一定要构建后测试加载反馈是否正常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 19:34:58