SPA 子域名根路径渲染(无重定向) 是做什么的?
问题
当构建基于子域名的路由时(例如 username.example.com),使用 React Router 的
<Navigate to="/profile/username" replace /> 会改变 URL,这违背了使用简洁子域名 URL 的初衷。此外,如果目标组件依赖于 useParams() 来获取根路径 / 上不存在的路由参数,则会显示空白页。
上下文 / 触发条件
- 子域名个人资料页面重定向到
/profile/npub1...,而不是直接在根路径提供服务内容 - 子域名根路径下页面显示为空白(无内容加载)
- 组件使用了
useParams(),但在没有这些参数的路由上渲染时失败 - 边缘工作者将用户数据注入到
window.__SUBDOMAIN_USER__或类似的全局变量中 - 希望
username.domain.com/渲染成类似个人网站的效果
解决方案
1. 路由器:直接渲染组件,而非导航重定向
之前(导致重定向 + URL 改变):
<Route path="/" element={
subdomainUser
? <Navigate to={`/profile/${subdomainUser.npub}`} replace />
: <Index />
} />
之后(根路径渲染,无 URL 改变):
<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/- URL 应保持为/,不重定向 - 个人资料内容应正确加载
- 检查浏览器控制台,不应出现关于缺失标识符的错误
- 在开发者工具中验证
window.__DIVINE_USER__包含预期的数据
示例
对于 divine-web:
thecomedianriley.divine.video/在根路径/渲染 ProfilePage- ProfilePage 首先检查
useParams()(在根路径/为空) - 回退到使用注入数据中的
getSubdomainUser()?.npub - 个人资料加载,URL 不变
注意事项
- 此模式需要边缘工作者和客户端代码之间的协调
- 边缘工作者必须在 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/