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

Next.js新手求助:实现页面初始加载时单次获取导航API数据并渲染以优化SEO及解决页面跳转刷新问题

解决方案:Next.js 动态导航栏(单次API调用+平滑路由+SEO友好)

让我们一步步解决你的问题,先从核心问题入手:

1. 修复API数据解析问题(导致整页刷新的关键原因)

你当前的getInitialProps里直接返回了fetch的响应对象res,而不是解析后的JSON数据。这会导致navitems是一个响应对象而非实际导航数据,最终让Link组件的href无效,触发普通a标签的整页跳转。

修改pages/_app.js的getInitialProps:

MyApp.getInitialProps = async (ctx) => {
  try {
    const res = await fetch("http://www.myserver.com/navigation");
    if (!res.ok) {
      throw new Error(`请求导航数据失败,状态码:${res.status}`);
    }
    // 必须解析响应为JSON格式
    const navigationItems = await res.json();
    return { navigationItems };
  } catch (error) {
    console.error("导航数据获取失败:", error);
    // 错误时返回空数组,避免组件崩溃
    return { navigationItems: [] };
  }
};

2. 确保Context导入路径正确

你在_app.js中定义了NavContext,但DesktopNav.js却从../../stores/NavContext导入,路径不匹配会导致Context失效。推荐将Context单独抽离到一个文件,避免路径混乱:

创建stores/NavContext.js:

import { createContext } from "react";

// 初始化Context为null,后续在Provider中注入数据
export const NavContext = createContext(null);

然后修改pages/_app.js导入这个Context:

import { useState } from "react";
import Layout from "../components/layout";
import "../styles/globals.css";
import { NavContext } from "../stores/NavContext"; // 导入抽离的Context

function MyApp({ Component, pageProps, navigationItems }) {
  const [navitems, setNavitems] = useState(navigationItems);
  return (
    <NavContext.Provider value={{ navitems }}>
      <Layout>
        <Component {...pageProps} />
      </Layout>
    </NavContext.Provider>
  );
}

// 保留上面修复后的getInitialProps
export default MyApp;

再修改components/Header/DesktopNav.js的导入:

import Link from "next/link";
import React, { useContext } from "react";
import { NavContext } from "../../stores/NavContext"; // 导入正确的Context
import DesktopSubMenuLoop from "./DesktopSubMenuLoop";

export default function DesktopNav() {
  const { navitems } = useContext(NavContext);

  // 数据未加载时显示占位内容
  if (!navitems || navitems.length === 0) {
    return <nav><ul><li>导航加载中...</li></ul></nav>;
  }

  return (
    <nav>
      <DesktopSubMenuLoop navitems={navitems} />
    </nav>
  );
}

3. 强化导航组件的稳定性(可选但推荐)

给DesktopSubMenuLoop增加数据校验,避免因数据格式错误导致渲染失败:

import Link from "next/link";
import React from "react";

export default function DesktopSubMenuLoop({ navitems, isSubmenu = false }) {
  // 校验数据是否为有效数组
  if (!navitems || !Array.isArray(navitems)) return null;

  return (
    <ul className={isSubmenu ? "sub-menu-style" : ""}>
      {navitems.map((item) => (
        <li key={item.id || item.title}> {/* 优先用唯一ID作为key */}
          <Link href={item.slug !== "/" ? `/${item.slug}` : item.slug}>
            <a>
              {item.title}{" "}
              {item.children && Array.isArray(item.children) && item.children.length > 0 && (
                <i className="icon-arrow-down"></i>
              )}
            </a>
          </Link>
          {item.children && Array.isArray(item.children) && item.children.length > 0 && (
            <DesktopSubMenuLoop navitems={item.children} isSubmenu={true} />
          )}
        </li>
      ))}
    </ul>
  );
}

方案优势说明

  • API仅调用一次:_app.js的getInitialProps会在服务器端首次渲染(页面刷新、首次访问)时执行一次,客户端通过Next.js Link导航时不会重复调用,实现全站共享导航数据。
  • 平滑路由:修复数据解析和Context匹配问题后,Link组件会正常触发Next.js的客户端导航,避免整页刷新。
  • SEO友好:导航数据在服务器端获取并渲染成完整HTML,爬虫可以直接抓取到导航内容,完全避免了useEffect客户端渲染的SEO劣势。

关于你提到的其他思路

  • 在_document.js中获取数据:不推荐,_document.js的作用是修改HTML根结构,不适合处理业务数据传递。
  • 每个页面用getServerSideProps:会导致所有页面变成SSR模式,且重复调用API,完全没必要,你的场景用_app.js的getInitialProps是最优解。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 09:22:38