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

基于Next.js将静态网站转为动态站,实现无头CMS驱动的动态导航方案

最优实现方案:静态站转Next.js+Strapi动态站点(导航自动更新)

针对你的需求,核心思路是用Strapi统一管理所有页面元数据与导航结构,Next.js通过API拉取数据动态渲染页面和导航,实现新增页面时导航自动同步。以下是分步实现细节:

一、Strapi数据模型设计

先在Strapi中搭建支撑动态导航和页面的核心模型:

  • NavigationItem模型:管理导航菜单的每一项,字段包括:
    • title:导航显示文本
    • slug:对应页面的路由路径
    • parent:关联自身模型(可选),用于实现多级菜单(比如「公司项目」下的分类)
    • isVisible:布尔值,控制是否在导航栏显示
    • sortOrder:数字,控制导航项的排序优先级
  • Page模型:对应每个页面的具体内容,字段包括:
    • slug:和NavigationItem的slug一一对应,确保路由匹配
    • title:页面标题
    • content:富文本,存储页面主体内容
    • pageType:枚举值(如home/about/project-category/news/job-form/contact-form),用于Next.js区分渲染不同组件
    • navigationItem:关联NavigationItem模型,确保页面和导航项绑定
  • 特殊表单模型:针对招聘/联系表单,可单独创建JobApplication和ContactMessage模型,用于存储用户提交的表单数据。

二、Next.js动态导航实现

在Next.js中构建可自动更新的导航组件,核心是从Strapi拉取并处理导航数据:

  1. 创建全局导航组件,可放在components/Navigation.js中:
import { useEffect, useState } from 'react';

export default function Navigation() {
  const [navItems, setNavItems] = useState([]);

  useEffect(() => {
    const fetchNavData = async () => {
      // 拉取已启用的导航项,按排序优先级排序,关联父级数据
      const res = await fetch(`${process.env.NEXT_PUBLIC_STRAPI_API_URL}/navigation-items?filters[isVisible][$eq]=true&sort=sortOrder:asc&populate=parent`);
      const { data } = await res.json();
      
      // 整理多级菜单结构,将子项挂载到父项的children属性
      const navMap = {};
      data.forEach(item => {
        const navItem = {
          id: item.id,
          title: item.attributes.title,
          slug: item.attributes.slug,
          parentId: item.attributes.parent?.data?.id
        };
        if (navItem.parentId) {
          navMap[navItem.parentId] = navMap[navItem.parentId] || { ...navMap[navItem.parentId], children: [] };
          navMap[navItem.parentId].children.push(navItem);
        } else {
          navMap[item.id] = { ...navItem, children: [] };
        }
      });
      setNavItems(Object.values(navMap));
    };
    fetchNavData();
  }, []);

  // 递归渲染多级导航菜单
  const renderNav = (items) => (
    <ul className="nav-list">
      {items.map(item => (
        <li key={item.id} className="nav-item">
          <a href={`/${item.slug}`}>{item.title}</a>
          {item.children.length > 0 && <ul className="sub-nav">{renderNav(item.children)}</ul>}
        </li>
      ))}
    </ul>
  );

  return <nav className="site-nav">{renderNav(navItems)}</nav>;
}
  1. 将导航组件引入_app.js或全局layout中,确保所有页面共享同一导航。
  2. 性能优化:若使用静态生成,可在getStaticProps中预取导航数据并传递给组件;或用SWR/React Query缓存导航数据,减少重复请求。

三、动态页面路由与渲染

利用Next.js的动态路由实现页面的自动生成:

  1. 创建pages/[slug].js作为所有动态页面的入口:
import HomePage from '../components/HomePage';
import AboutPage from '../components/AboutPage';
import ProjectCategoryPage from '../components/ProjectCategoryPage';
import NewsPage from '../components/NewsPage';
import JobFormPage from '../components/JobFormPage';
import ContactFormPage from '../components/ContactFormPage';

// 预生成已存在的页面路由
export async function getStaticPaths() {
  const res = await fetch(`${process.env.NEXT_PUBLIC_STRAPI_API_URL}/pages?fields[0]=slug`);
  const { data } = await res.json();
  const paths = data.map(page => ({
    params: { slug: page.attributes.slug }
  }));
  return {
    paths,
    fallback: 'blocking' // 新页面自动触发静态生成,避免404
  };
}

// 根据slug拉取对应页面数据
export async function getStaticProps({ params }) {
  const res = await fetch(`${process.env.NEXT_PUBLIC_STRAPI_API_URL}/pages?filters[slug][$eq]=${params.slug}&populate=*`);
  const { data } = await res.json();
  const page = data[0];
  
  if (!page) return { notFound: true };
  
  return {
    props: { pageData: page.attributes },
    revalidate: 60 // 每60秒自动验证更新内容
  };
}

// 根据pageType渲染对应组件
export default function DynamicPage({ pageData }) {
  switch(pageData.pageType) {
    case 'home':
      return <HomePage content={pageData.content} />;
    case 'about':
      return <AboutPage content={pageData.content} />;
    case 'project-category':
      return <ProjectCategoryPage content={pageData.content} />;
    case 'news':
      return <NewsPage content={pageData.content} articles={pageData.newsArticles?.data} />;
    case 'job-form':
      return <JobFormPage />;
    case 'contact-form':
      return <ContactFormPage />;
    default:
      return <div className="page-not-found">页面不存在</div>;
  }
}
  1. 针对新闻文章这类子内容,可在Strapi中创建NewsArticle模型并关联到Page模型的newsArticles字段,在Next.js的新闻页面中渲染文章列表。

四、表单提交处理

招聘和联系表单通过Next.js API Routes实现后端逻辑:

  1. 创建pages/api/submit-job.js处理求职表单提交:
export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ message: '仅支持POST请求' });
  }
  
  try {
    const { name, email, resume, message } = req.body;
    // 调用Strapi API存储求职数据
    const strapiRes = await fetch(`${process.env.STRAPI_API_URL}/job-applications`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${process.env.STRAPI_API_TOKEN}`
      },
      body: JSON.stringify({
        data: { name, email, resume, message }
      })
    });
    
    if (!strapiRes.ok) throw new Error('提交失败');
    res.status(200).json({ message: '求职申请提交成功' });
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
}
  1. 在表单组件中调用该API完成提交,类似逻辑可复用在联系表单上。

五、进阶优化

  • 实时更新:在Strapi中配置Webhook,当导航或页面数据变更时,调用Next.js的revalidate API,触发对应页面的重新生成,无需等待ISR时间。
  • 环境变量:将Strapi API地址、令牌等敏感信息存入环境变量(.env.local),避免硬编码。
  • 缓存策略:用SWR/React Query对页面和导航数据做缓存,提升前端加载速度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 22:35:27