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

如何在Next.js框架中从SpringBoot后端API获取数据渲染记录表格?

Next.js + SpringBoot 实现数据表格的最优方案

一、后端SpringBoot接口准备

优先提供支持分页、排序的REST接口,这是大数量级数据场景下的性能基础。SpringBoot可通过Pageable自动解析分页参数,返回标准化的分页数据结构。

示例Controller代码:

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/records")
public class RecordController {

    private final RecordService recordService;

    public RecordController(RecordService recordService) {
        this.recordService = recordService;
    }

    // 分页获取记录,自动解析page/size/sort参数
    @GetMapping
    public Page<Record> getRecords(Pageable pageable) {
        return recordService.findAll(pageable);
    }
}

同时配置CORS允许前端访问:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("http://localhost:3000"); // 你的Next.js前端地址
        config.addAllowedMethod("*");
        config.addAllowedHeader("*");
        source.registerCorsConfiguration("/api/**", config);
        return new CorsFilter(source);
    }
}

二、前端Next.js数据获取:用专业工具提升效率

Next.js中推荐用SWR或React Query处理数据请求,它们自带缓存、重新验证、错误处理能力,比原生fetch更高效。这里以SWR为例:

  1. 安装SWR:
npm install swr
  1. 封装请求逻辑,处理分页排序参数:
import useSWR from 'swr';
import { useState } from 'react';

const fetcher = async (url) => {
  const res = await fetch(url);
  if (!res.ok) throw new Error('请求失败');
  return res.json();
};

export default function RecordTable() {
  // 分页与排序状态
  const [pageIndex, setPageIndex] = useState(0);
  const [pageSize, setPageSize] = useState(10);
  const [sortBy, setSortBy] = useState('id');
  const [sortDir, setSortDir] = useState('asc');

  // 构造请求URL
  const url = `http://localhost:8080/api/records?page=${pageIndex}&size=${pageSize}&sort=${sortBy},${sortDir}`;
  
  const { data, error, isLoading } = useSWR(url, fetcher);

  // 状态反馈
  if (isLoading) return <div>加载中...</div>;
  if (error) return <div>获取数据失败:{error.message}</div>;

  // 表格渲染逻辑(后续结合表格库实现)
  return <div className="table-wrapper"></div>;
}

如果有SEO需求,用getServerSideProps做服务器端渲染:

export async function getServerSideProps(context) {
  const { page = 0, size = 10 } = context.query;
  const res = await fetch(`http://localhost:8080/api/records?page=${page}&size=${size}`);
  const data = await res.json();
  return { props: { data } };
}

export default function RecordTable({ data }) {
  // 直接使用服务端获取的数据渲染
  return <div className="table-wrapper"></div>;
}

三、表格渲染:用高性能可定制的库

推荐TanStack Table(原React Table),它轻量无UI依赖,支持分页、排序、过滤、虚拟滚动等核心功能,性能远超原生table。

  1. 安装TanStack Table:
npm install @tanstack/react-table
  1. 结合SWR实现完整表格:
import useSWR from 'swr';
import { useState, useEffect } from 'react';
import { useTable, usePagination, useSortBy } from '@tanstack/react-table';

const fetcher = async (url) => {
  const res = await fetch(url);
  if (!res.ok) throw new Error('请求失败');
  return res.json();
};

// 定义表格列
const columns = [
  { accessorKey: 'id', header: 'ID', enableSorting: true },
  { accessorKey: 'name', header: '名称', enableSorting: true },
  { accessorKey: 'createTime', header: '创建时间', enableSorting: true }
];

export default function RecordTable() {
  const [pageIndex, setPageIndex] = useState(0);
  const [pageSize, setPageSize] = useState(10);
  const [sortInfo, setSortInfo] = useState({ sortBy: 'id', sortDir: 'asc' });

  const url = `http://localhost:8080/api/records?page=${pageIndex}&size=${pageSize}&sort=${sortInfo.sortBy},${sortInfo.sortDir}`;
  const { data, error, isLoading } = useSWR(url, fetcher);

  if (isLoading) return <div>加载中...</div>;
  if (error) return <div>获取数据失败:{error.message}</div>;

  // 配置表格实例
  const table = useTable({
    columns,
    data: data.content,
    pageCount: Math.ceil(data.totalElements / pageSize),
    initialState: { pagination: { pageIndex, pageSize } }
  }, useSortBy, usePagination);

  // 同步表格状态到前端
  useEffect(() => {
    const pagination = table.getState().pagination;
    setPageIndex(pagination.pageIndex);
    setPageSize(pagination.pageSize);

    const sort = table.getState().sorting[0];
    if (sort) {
      setSortInfo({
        sortBy: sort.id,
        sortDir: sort.desc ? 'desc' : 'asc'
      });
    }
  }, [table.getState()]);

  return (
    <div className="table-container">
      <table border="1">
        <thead>
          {table.getHeaderGroups().map(headerGroup => (
            <tr key={headerGroup.id}>
              {headerGroup.headers.map(column => (
                <th key={column.id} onClick={column.getSortHandler()}>
                  {column.render('header')}
                  {column.getIsSorted() ? (column.getIsSorted() === 'desc' ? ' ↓' : ' ↑') : ''}
                </th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody>
          {table.getRowModel().rows.map(row => (
            <tr key={row.id}>
              {row.getVisibleCells().map(cell => (
                <td key={cell.id}>{cell.render('cell')}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>

      {/* 分页控件 */}
      <div className="pagination">
        <button onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}>上一页</button>
        <span>第 {pageIndex + 1} 页 / 共 {table.getPageCount()} 页</span>
        <button onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}>下一页</button>
        <select value={pageSize} onChange={(e) => table.setPageSize(Number(e.target.value))}>
          {[10, 20, 50].map(size => <option key={size} value={size}>{size} 条/页</option>)}
        </select>
      </div>
    </div>
  );
}

四、关键优化点

  • 分页优先:永远不要一次性请求全量数据,通过后端分页+前端控件,只加载当前页数据
  • 缓存复用:SWR/React Query会缓存请求结果,用户切换页面返回时直接复用缓存
  • 虚拟滚动:单页数据超200条时,用@tanstack/react-virtual插件实现虚拟滚动,只渲染可视区域行
  • 搜索防抖:实现搜索功能时,对输入做500ms防抖处理,避免频繁调用后端
  • 状态反馈:始终处理加载、错误状态,给用户明确提示,避免页面空白

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 14:29:10