Communitygithub.com

SPA子域名根目錄渲染(無重定向)

在React Router SPA中,直接在子域名根路徑(/)渲染頁面元件,避免URL重新導向。

SPA子域名根目錄渲染(無重定向) 是什麼?

這項技能解決了在React Router SPA中,當使用子域名路由(例如 `username.example.com`)時,常見的頁面空白或URL重新導向問題。通常,開發者會使用 `<Navigate to="/profile/username" replace />` 來處理子域名內容,但這會改變URL,破壞乾淨的子域名體驗。此外,依賴 `useParams()` 的元件在根路徑 (`/`) 缺少路由參數時,可能導致頁面空白。 此技能提供了一套完整的解決方案,涵蓋路由設定、元件資料回退、邊緣工作者資料注入和客戶端資料存取。核心模式是:在路由器中直接渲染目標元件而非進行導航;在元件內部,先檢查路由參數,若無則從 `window.__DIVINE_USER__` 此類全域變數取得由邊緣工作者注入的資料;邊緣工作者(如 Fastly Compute、Cloudflare Workers)在回應HTML前,將使用者資料注入 `<head>` 中。 此模式適用於任何接受子域名的React SPA,尤其適合想讓 `username.domain.com/` 呈現個人網站風格的應用。它支援 Fastly Compute、Cloudflare Workers 等邊緣運算平台,並確保SEO與使用者體驗最佳化。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/divinevideo/divine-web/tree/HEAD/.agents/skills/spa-subdomain-root-render

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

SPA 子域名根目錄渲染(無重新導向)

問題

在建構基於子域名的路由時(例如 username.example.com),使用 React Router 的 <Navigate to="/profile/username" replace /> 會改變網址,這違背了擁有乾淨子域名網址的目的。此外,目標元件若依賴 useParams() 來取得路由參數,但這些參數在根路徑 (/) 不存在時,可能會顯示空白頁面。

情境 / 觸發條件

  • 子域名個人檔案頁面被重新導向至 /profile/npub1...,而非在 / 提供內容
  • 子域名根目錄下頁面顯示空白(未載入任何內容)
  • 元件使用 useParams(),但渲染於缺少這些參數的路由時失敗
  • 邊緣工作者將使用者資料注入 window.__SUBDOMAIN_USER__ 或類似全域變數
  • 希望 username.domain.com/ 呈現個人網站風格

解決方案

1. 路由器:直接渲染元件而非進行導航

修改前(導致重新導向 + 網址改變):

<Route path="/" element={
  subdomainUser
    ? <Navigate to={`/profile/${subdomainUser.npub}`} replace />
    : <Index />
} />

修改後(在 / 渲染,網址不變):

<Route path="/" element={
  subdomainUser
    ? <ProfilePage />
    : <Index />
} />

2. 元件:新增資料備援來源

元件必須在缺少路由參數時檢查注入的子域名資料。

修改前(僅檢查路由參數):

export function ProfilePage() {
  const { npub } = useParams<{ npub?: string }>();
  const identifier = npub;

  if (!identifier) {
    return <Error message="未提供使用者識別碼" />;
  }
  // ... 元件其餘部分
}

修改後(備援至注入資料):

import { getSubdomainUser } from '@/hooks/useSubdomainUser';

export function ProfilePage() {
  const { npub } = useParams<{ npub?: string }>();
  const subdomainUser = getSubdomainUser();

  // 路由參數優先,子域名資料為備援
  const identifier = npub || subdomainUser?.npub;

  if (!identifier) {
    return <Error message="未提供使用者識別碼" />;
  }
  // ... 元件其餘部分
}

3. 邊緣工作者:將使用者資料注入 HTML

邊緣工作者(Fastly Compute、Cloudflare Workers 等)應注入使用者資料:

// 在處理子域名的邊緣工作者中
const userData = await fetchUserData(username);
const html = await fetchSpaHtml();

const injectedHtml = html.replace(
  '</head>',
  `<script>window.__DIVINE_USER__ = ${JSON.stringify(userData)};</script></head>`
);

return new Response(injectedHtml, { headers: { 'content-type': 'text/html' } });

4. 客戶端資料存取

// useSubdomainUser.ts
interface SubdomainUser {
  subdomain: string;
  pubkey: string;
  npub: string;
  username?: string;
  displayName?: string;
  // ... 其他欄位
}

export function getSubdomainUser(): SubdomainUser | null {
  if (typeof window === 'undefined') return null;
  return (window as any).__DIVINE_USER__ || null;
}

驗證

  1. 訪問 username.domain.com/ - 網址應停留在 /,不應重新導向
  2. 個人檔案內容應正常載入
  3. 在瀏覽器控制台檢查是否有關於缺少識別碼的錯誤
  4. 在開發者工具中驗證 window.__DIVINE_USER__ 包含預期資料

範例

以 divine-web 為例:

  • thecomedianriley.divine.video/ 在 / 渲染 ProfilePage
  • ProfilePage 先檢查 useParams()(在 / 為空)
  • 備援至從注入資料取得的 getSubdomainUser()?.npub
  • 個人檔案載入,網址不變

注意事項

  • 此模式需要邊緣工作者與客戶端程式碼協調
  • 邊緣工作者必須在 SPA 水合(hydration)前注入資料
  • 始終優先檢查路由參數,以確保正常的 /profile/:npub 路由能正常運作
  • 考慮 SEO:邊緣工作者也應設定適當的 meta 標籤
  • 子域名上的連結可使用 SmartLink 元件,對擁有者的內容保持本機路徑,但對其他使用者的內容則連結到頂級域名

參考資料

相關技能