fix(seo): 详情页服务端渲染写入静态HTML并修复skills预渲染报错

- courses/skills/contents/practices 详情页改为服务端取数 + initialData 渲染,正文进入静态 HTML(解决百度弱 JS 收录盲点)
- contents 的 markdown 由 useEffect+setState 改为 useMemo 同步输出
- 四个 generateStaticParams 改用 API 真实 id;getX 校验 data.id 防后端 200+error 体被当有效数据导致 undefined.map 报错
- 更新进度文档至 v1.3.0
This commit is contained in:
TradeMate Dev
2026-07-11 17:46:31 +08:00
parent 09bf329544
commit 6850ceff56
9 changed files with 117 additions and 73 deletions
+7 -6
View File
@@ -16,20 +16,21 @@ interface Skill {
}
interface AffiliateLink { id: number; title: string; description: string | null; url: string }
export default function SkillDetailPage() {
export default function SkillDetailPage({ initialSkill }: { initialSkill?: Skill | null }) {
const params = useParams();
const t = useT();
const [skill, setSkill] = useState<Skill | null>(null);
const [loading, setLoading] = useState(true);
const [skill, setSkill] = useState<Skill | null>(initialSkill ?? null);
const [loading, setLoading] = useState(!initialSkill);
const [links, setLinks] = useState<AffiliateLink[]>([]);
useEffect(() => {
if (initialSkill) return;
if (!params.id) return;
fetch(`${API_BASE}/skills/${params.id}`)
.then(r => r.json())
.then(data => { if (data.id) setSkill(data); })
.then((r) => r.json())
.then((data) => { if (data.id) setSkill(data); })
.finally(() => setLoading(false));
}, [params.id]);
}, [params.id, initialSkill]);
useEffect(() => {
if (!skill?.id) return;