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

shi0318

大多數早期的遊戲 Wiki 都存在信任問題。頁面混雜官方事實、Beta 發現、預告推測,以及社群假設,卻沒有明確告訴讀者哪些內容經過確認。為了改善我所關注遊戲的 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

對於發售前內容,優先考量如下:快速首屏渲染、極小的 JavaScript、自動生成 Sitemap、可預測的部署,以及無需開啟 CMS 即可輕鬆撰寫內容。Astro 5 符合上述所有需求。


text
output: 'static',
trailingSlash: 'always',
integrations: [sitemap({ ... })],
vite: { plugins: [tailwindcss()] }
這基本上就是全部設定。沒有 runtime、沒有 Workers、沒有資料庫。頁面渲染為純 HTML,直接從 Cloudflare 邊緣節點發送。
信心標籤內容模型
讓這些 Wiki 與一般靜態內容不同的關鍵,在於小型的內容合約。每個指南檔案都在 frontmatter 中帶有狀態:
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 摘要時,將頁面從 Sitemap 中排除。
從 git 取得每頁的 sitemap lastmod
我最滿意的小技巧是每頁的 lastmod。Astro 的 sitemap 整合預設將 lastmod 設為建置時間。對於由六個 Wiki 組成的網路,只要任一頁面變更就會重新部署,這對 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。
將弱勢頁面從 sitemap 中過濾
推測性頁面適合在站內瀏覽,但不應浪費爬蟲預算。我保留一份小型的 NOINDEX_URLS 白名單,並以與頁面 meta 標籤相同的方式過濾 sitemap:
const NOINDEX_URLS = ['/gloom/'];

sitemap({
  filter: page => !NOINDEX_URLS.includes(new URL(page).pathname),
})
因此標記為 trailer 或 unconfirmed 的頁面仍可存在於導覽中,但只有已驗證的頁面才會參與 Google 競爭。
為什麼這個網路適合遊戲發售前內容
有三個原因讓此模式值得複製:
狀態作為一等公民。發售前內容不斷變化。將其明確建模在內容 schema 中,可強制誠實撰寫,並自動化哪些內容適合發布。

全靜態。無資料庫、無 API、無 Workers。Cloudflare Pages 免費處理整個網路。

開源作為護城河。任何人皆可 fork 儲存庫、替換資料,並在週末為自己的利基遊戲推出 Wiki。這就是六個小型 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