How I Built a Source-Tracked Game Wiki Network with Astro 5 的封面图片

shi0318

大多数早期的游戏 Wiki 都存在信任问题。页面将官方事实、内测发现、预告片猜测和社区推测混杂在一起,却不告诉读者哪些内容真正得到了确认。为了解决我所关注的游戏的这个问题,我用 Astro 5 搭建了一个小型的来源追踪游戏 Wiki 网络。

本文将介绍该网络的架构、内容模型,以及让整个网络托管成本低廉、易于更新的小技巧。

我实际搭建的内容

六个独立的 Wiki,全部使用同一套技术栈:

仓库均为开源,可直接阅读代码并复用该模式:

  • github.com/shi0318/MortalShell2
  • github.com/shi0318/ValorMortis
  • github.com/shi0318/plaguetale
  • github.com/shi0318/beastreincarnationwiki
  • github.com/shi0318/graveseasons
  • github.com/shi0318/StarWarsGalacticRacer

为什么用 Astro 5 做发售前游戏 Wiki

对于发售前内容,优先级很明确:首屏渲染快、JS 体积小、自动生成站点地图、可预测的部署,以及无需打开 CMS 即可轻松编写内容。Astro 5 完全满足这些需求。


text
output: 'static',
trailingSlash: 'always',
integrations: [sitemap({ ... })],
vite: { plugins: [tailwindcss()] }
以上基本就是全部配置。没有运行时、没有 Workers、没有数据库。页面渲染为纯 HTML,直接通过 Cloudflare 边缘节点提供服务。
置信度标签的内容模型
让这些 Wiki 与普通静态站点不同的关键在于一个简洁的内容契约。每篇攻略文件都在 frontmatter 中携带一个 status 字段:
status: official
status: beta
status: trailer
status: series
status: unconfirmed
<StatusBadge> 组件会将该状态渲染为可见标签。<SourceTable> 组件则列出来源 URL、核查日期和备注,让读者一目了然地知道一份 Boss 列表是经 Steam 确认、由预告片推断,还是来自论坛传闻。
代码示例:
import { z } from 'astro:content';

const guideSchema = z.object({
  title: z.string().max(60),
  description: z.string().min(50).max(160),
  status: z.enum(['official', 'beta', 'trailer', 'series', 'unconfirmed']),
  sources: z.array(z.object({
    url: z.string().url(),
    date: z.date(),
    note: z.string(),
  })),
});
这带来两个好处:第一,站点不会把猜测悄无声息地当作事实发布;第二,同一 schema 可在状态太弱不适合出现在 Google 摘要时自动将页面从站点地图中剔除。
基于 git 的单页 sitemap lastmod
我最满意的技巧是单页 lastmod。Astro 的 sitemap 集成默认使用构建时间作为 lastmod。对于只要任意一页更新就会重新部署的六站网络来说,这对 Google 来说是一个很弱的信号。
因此我覆写 serialize 方法,将每个 URL 映射回源文件,再通过 git 查询该文件的最后一次提交:
import { execFileSync } from 'node:child_process';
import { existsSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';

function lastmodFor(url) {
  const slug = new URL(url).pathname.replace(/^\/+|\/+$/g, '');
  const candidates = slug === ''
    ? ['src/pages/index.astro']
    : [
        `src/pages/${slug}.astro`,
        `src/pages/${slug}/index.astro`,
        `src/content/guides/${slug}.md`,
        `src/content/guides/${slug}.mdx`,
      ];
  const file = candidates.find(rel => existsSync(join(ROOT, rel)));
  if (!file) return BUILD_TIME;
  try {
    return execFileSync(
      'git',
      ['log', '-1', '--format=%cI', '--', file],
      { cwd: ROOT, encoding: 'utf8' }
    ).trim();
  } catch {
    return BUILD_TIME;
  }
}

sitemap({
  serialize(item) {
    return { ...item, lastmod: lastmodFor(item.url) };
  },
})
结果:只有真正被编辑过的页面时间戳会更新,其余页面保持不变。这为 Google 提供了一个干净的时效性信号,而无需使用 CMS。
将弱置信页面从站点地图中过滤
推测性页面对站内浏览仍有价值,但不应浪费爬取预算。我维护一个小型 NOINDEX_URLS 白名单,并以与页面 meta 标签相同的规则过滤站点地图:
const NOINDEX_URLS = ['/gloom/'];

sitemap({
  filter: page => !NOINDEX_URLS.includes(new URL(page).pathname),
})
因此,标记为 trailer 或 unconfirmed 的页面仍可存在于导航中,但只有已验证页面才会参与 Google 收录竞争。
为什么该网络适合发售前游戏
有三点让这个模式值得复制:
Status 作为一等概念。发售前内容不断变化,在内容 schema 中显式建模这一概念,可强制诚实的写作,并自动化控制哪些内容适合发布。

一切静态化。没有数据库、没有 API、没有 Workers。Cloudflare Pages 免费托管整个网络。

开源即护城河。任何人均可 fork 仓库、替换数据,并在周末为自己的小众游戏搭建 Wiki。这就是六站小网络如何成为一种可复制模式的原因。

如果你想在自己的游戏上尝试,最简单的路径是:
git clone https://github.com/shi0318/MortalShell2.git my-game-wiki
cd my-game-wiki
npm install
npm run dev
然后编辑 src/data/site.ts,将你的页面放入 src/content/guides/ 并部署。
如果你想关注网络的成长,启动中心是 Mortal Shell II Wiki。欢迎在任意一个仓库提交反馈和 PR。

Enter fullscreen mode Exit fullscreen mode