成为全栈·Next.js 网站前台篇·站点设置如何驱动页头、页脚与 SEO

成为全栈·Next.js 网站前台篇·站点设置如何驱动页头、页脚与 SEO

站点名称、Logo、描述、关键词和版权不是散落在组件里的常量。它们是一份公开配置,需要同时驱动页面外壳和 metadata,并在接口失败或字段为空时有一致降级。

前言

管理后台已经允许修改站点设置,如果前台页头、页脚和 SEO 仍各写一份默认文案,运营人员改一次名称就会得到三个版本。更隐蔽的问题是:Logo URL 来自配置,若不限制协议,公共页头可能渲染危险或无效地址。

当前项目用 siteChrome() 作为页面外壳的公开数据入口,让 Header、Footer 和根 metadata 读取同一份设置,并把失败降级集中起来。

配置字段各有消费位置

字段 页面消费 SEO 消费 空值策略
siteName 页头品牌、页脚默认版权 标题模板 使用项目默认名称
siteTitle --- 默认页面标题 回退 siteName
siteDescription 首页介绍 description 使用默认描述
siteKeywords --- keywords 空时省略
logoUrl 页头图标 可扩展分享图 无效时使用字母标记
copyright 页脚 --- 年份 + siteName

请求内复用与失败降级集中处理

ts 复制代码
export const settings = cache(getSiteSettings)
export const categories = cache(getCategoryTree)

export const fallbackSite = {
  siteName: '成为全栈开发工程师',
  siteTitle: '',
  siteKeywords: '',
  siteDescription: '用一个真实系统,串起全栈开发的每一步。',
}

export const siteChrome = cache(async () => ({
  site: await settings().catch(() => fallbackSite),
  categories: await categories().catch(() => []),
}))

页头属于可选外壳,设置接口暂时失败时可以使用默认身份;文章正文则不能用空对象降级。错误策略取决于模块是否为核心任务,而不是"所有 GET 都 catch"。

React cache() 避免同一次渲染中 Header、Footer 与 metadata 重复计算。底层公开 fetch 仍负责跨请求 60 秒再验证,两层职责不同。

根 metadata 使用同一份设置

tsx 复制代码
export async function generateMetadata(): Promise<Metadata> {
  const { site } = await siteChrome()

  return {
    metadataBase: new URL(
      process.env.NEXT_PUBLIC_SITE_URL || 'http://127.0.0.1:13001',
    ),
    title: {
      default: site.siteTitle || site.siteName || '成为全栈开发工程师',
      template: `%s · ${site.siteName}`,
    },
    keywords: site.siteKeywords || undefined,
    description: site.siteDescription || fallbackSite.siteDescription,
  }
}

页面级 metadata 只覆盖文章标题和摘要,根 layout 继续提供标题模板与 metadataBase。正式构建必须配置生产域名,否则 canonical 和分享地址可能指向本机。

ts 复制代码
export function safeLink(value?: string | null): string | undefined {
  if (!value || /[\\\r\n]/.test(value)) return undefined
  if (value.startsWith('/') && !value.startsWith('//')) return value

  try {
    const url = new URL(value)
    return ['https:', 'http:'].includes(url.protocol) ? url.href : undefined
  } catch {
    return undefined
  }
}
tsx 复制代码
const logo = 'logoUrl' in site ? safeLink(site.logoUrl) : undefined

{logo ? (
  <Picture src={logo} alt="" />
) : (
  <span className="mark">F</span>
)}

Logo 旁边已有站点名称,因此图片 alt 为空,避免读屏重复品牌。无效地址回退字母标记,不渲染破损图片。

tsx 复制代码
<footer>
  <nav aria-label="页脚导航">
    <Link href="/about">关于网站</Link>
    <Link href="/articles">全部文章</Link>
    <Link href="/categories">分类导航</Link>
    <Link href="/tags">标签索引</Link>
  </nav>
  <div>
    {site.copyright
      ? site.copyright
      : `© ${new Date().getFullYear()} ${site.siteName}`}
  </div>
</footer>

配置字段为空时才回退,不用 || 随意覆盖合法的短文案。版权属于展示配置,不应在代码里硬编码某个年份长期不变。

60 秒更新窗口是一项业务承诺

ts 复制代码
return fetch(url, {
  cache: 'force-cache',
  next: { revalidate: 60, tags: ['site-settings'] },
})

后台保存设置后,前台可能在再验证窗口内继续显示旧值,第一个过期请求还可能先拿旧值并触发后台更新。这个行为需要在后台提示或运营流程里被接受。

若 Logo 或站点名称要求立即切换,应增加受保护的按需失效通道,而不是把所有公开请求改为 no-store。

配置变更要跨页面核对

text 复制代码
1. 将 siteName 从 A 改为 B
2. 在 TTL 内检查旧值仍可能存在
3. 过期并完成再验证后检查 Header、Footer、首页 title
4. 打开文章详情,检查标题模板变为"文章标题 · B"
5. 将 logoUrl 改为非法协议,确认回退标记
6. 清空 copyright,确认年份与站名回退
7. 停止设置接口,确认正文仍可读且外壳使用默认配置
故障 合理表现
设置接口 500 外壳用默认值,核心内容继续
分类接口 500 导航退化为基本入口
Logo 404 图片组件失败策略或标记回退需检查
配置为空字符串 按字段定义省略或回退
正式域名缺失 构建虽可运行,但不得当作上线产物

适用边界

当前设置是全站单租户配置。如果未来支持多域名、多语言或多品牌,缓存键必须包含租户与 locale,不能继续使用一个全局 siteChrome() 结果。

频繁运营位也不适合无限加入站点设置。首页焦点、活动 Banner 应有生效时间与独立模型,避免一个配置对象变成无边界 CMS。

小结

站点设置的价值不是减少几处常量,而是建立单一公开身份:Header、Footer 与 SEO 读取同一事实,空值和失败使用同一降级规则,更新窗口也有明确预期。

配置驱动只有在消费位置、缓存时序和安全过滤一起成立时,才真正比硬编码可靠。

延伸阅读

  • Next.js SEO
  • 会员公开主页与通知中心
  • 响应式、可访问性与错误状态

如果这篇文章对你有帮助,欢迎订阅我的 CSDN 专栏 「成为全栈」:

🔗 专栏地址:https://blog.csdn.net/fungleo/category_13204651.html

📦 本系列配套代码仓库:https://github.com/fengcms/become-a-full-stack-developer

相关推荐
大连好光景5 小时前
ReAct的原理?与CoT的区别是啥?
react·cot
大连好光景5 小时前
Agent完整工作过程
react·agent完整工作流程
FungLeo2 天前
成为全栈·Next.js 网站前台篇·同源 BFF 代理:API、附件、Cookie 与跨站写入保护
react·csrf·next.js·bff·成为全栈·route handler
FungLeo2 天前
成为全栈·Next.js 网站前台篇·评论系统:叠楼、回复、删除与内容审核如何落到前台
react.js·内容审核·next.js·评论系统·树形数据·成为全栈
Alice-YUE2 天前
React 渲染机制速通:Render 树、Fiber 树与更新调度
前端·react.js·前端框架·react·fiber·前端性能·渲染机制
传奇开心果编程3 天前
【现代声明式UI学与练】第1课 从命令式UI到声明式UI
学习·flutter·ui·swiftui·react·android jetpack
frjc3 天前
前端技术选型:主流方案对比与落地建议
vue·react·angular
ly76893 天前
ISR 收缩与 HW 推进:副本同步的边界条件与 UnderReplicated 排障
数据库·kafka·c#·linq·isr·hw·副本同步
cindershade5 天前
一条请求的接力赛:用运行时责任重画 RSC 边界
性能优化·next.js