大多数早期的游戏 Wiki 都存在信任问题。页面将官方事实、内测发现、预告片猜测和社区推测混杂在一起,却不告诉读者哪些内容真正得到了确认。为了解决我所关注的游戏的这个问题,我用 Astro 5 搭建了一个小型的来源追踪游戏 Wiki 网络。
本文将介绍该网络的架构、内容模型,以及让整个网络托管成本低廉、易于更新的小技巧。
我实际搭建的内容
六个独立的 Wiki,全部使用同一套技术栈:
- Mortal Shell II Wiki — Mortal Shell II 的已验证攻略、内测笔记、外壳、武器、 Boss
- Valor Mortis Demo Guide — One More Level 魂类游戏的来源追踪试玩指南
- Resonance: A Plague Tale Legacy Guide — 《A Plague Tale Legacy》的故事、角色、战斗、流程规划
- Beast of Reincarnation Wiki — Game Freak 的动作 RPG,Emma 与 Koo、Boss、Build
- Grave Seasons Guide — Ashenridge 的农场谋杀推理、恋爱、流程
- Star Wars Galactic Racer Wiki — 带置信度标签的《星球大战:银河赛车》发售前 Wiki
仓库均为开源,可直接阅读代码并复用该模式:
github.com/shi0318/MortalShell2github.com/shi0318/ValorMortisgithub.com/shi0318/plaguetalegithub.com/shi0318/beastreincarnationwikigithub.com/shi0318/graveseasonsgithub.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
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.