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;
}
驗證
- 訪問
username.domain.com/- 網址應停留在/,不應重新導向 - 個人檔案內容應正常載入
- 在瀏覽器控制台檢查是否有關於缺少識別碼的錯誤
- 在開發者工具中驗證
window.__DIVINE_USER__包含預期資料
範例
以 divine-web 為例:
thecomedianriley.divine.video/在/渲染 ProfilePage- ProfilePage 先檢查
useParams()(在/為空) - 備援至從注入資料取得的
getSubdomainUser()?.npub - 個人檔案載入,網址不變
注意事項
- 此模式需要邊緣工作者與客戶端程式碼協調
- 邊緣工作者必須在 SPA 水合(hydration)前注入資料
- 始終優先檢查路由參數,以確保正常的
/profile/:npub路由能正常運作 - 考慮 SEO:邊緣工作者也應設定適當的 meta 標籤
- 子域名上的連結可使用
SmartLink元件,對擁有者的內容保持本機路徑,但對其他使用者的內容則連結到頂級域名
參考資料
- React Router v6 Routes:https://reactrouter.com/en/main/route/route
- Fastly Compute@Edge:https://developer.fastly.com/learning/compute/