From 83f7f3d6d1c47820fd209902d310d07914999578 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 11 Feb 2026 23:19:35 +0800 Subject: [PATCH] fix login crash from DOM cleanup race --- components/antigravity-background.tsx | 14 +++++- components/favicon-updater.tsx | 65 +++++++++++++++------------ 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/components/antigravity-background.tsx b/components/antigravity-background.tsx index 4b4e13d..a477888 100644 --- a/components/antigravity-background.tsx +++ b/components/antigravity-background.tsx @@ -23,13 +23,20 @@ export function AntigravityBackground() { useEffect(() => { if (!mounted || !sceneRef.current) return; + const safeRemoveCanvas = (render: Matter.Render | null) => { + const canvas = render?.canvas; + if (canvas?.parentNode) { + canvas.parentNode.removeChild(canvas); + } + }; + // 清理旧实例 if (engineRef.current) { Matter.Engine.clear(engineRef.current); } if (renderRef.current) { Matter.Render.stop(renderRef.current); - renderRef.current.canvas.remove(); + safeRemoveCanvas(renderRef.current); } // 创建物理引擎 @@ -224,15 +231,18 @@ export function AntigravityBackground() { if (mouseConstraintRef.current) { Matter.Composite.remove(engine.world, mouseConstraintRef.current); + mouseConstraintRef.current = null; } if (renderRef.current) { Matter.Render.stop(renderRef.current); - renderRef.current.canvas.remove(); + safeRemoveCanvas(renderRef.current); + renderRef.current = null; } if (engineRef.current) { Matter.Engine.clear(engineRef.current); + engineRef.current = null; } Matter.Runner.stop(runner); diff --git a/components/favicon-updater.tsx b/components/favicon-updater.tsx index 5d68bd2..43d127e 100644 --- a/components/favicon-updater.tsx +++ b/components/favicon-updater.tsx @@ -4,44 +4,53 @@ import { useEffect } from "react"; export function FaviconUpdater() { useEffect(() => { + let isDisposed = false; + // 从 API 获取最新的 favicon 设置 fetch("/api/site-settings") .then((res) => res.json()) .then((settings) => { - if (settings.favicon) { - const faviconUrl = settings.favicon; - const timestamp = Date.now(); - const urlWithCache = `${faviconUrl}${faviconUrl.includes("?") ? "&" : "?"}v=${timestamp}`; - - // 移除所有旧的 favicon link 标签 - const oldLinks = document.querySelectorAll('link[rel*="icon"]'); - oldLinks.forEach((link) => link.remove()); - - // 添加新的 favicon link 标签 - const link = document.createElement("link"); - link.rel = "icon"; - link.type = "image/x-icon"; - link.href = urlWithCache; - document.head.appendChild(link); - - // 添加 Apple touch icon - const appleLink = document.createElement("link"); - appleLink.rel = "apple-touch-icon"; - appleLink.href = urlWithCache; - document.head.appendChild(appleLink); - - // 添加 shortcut icon - const shortcutLink = document.createElement("link"); - shortcutLink.rel = "shortcut icon"; - shortcutLink.href = urlWithCache; - document.head.appendChild(shortcutLink); + if (!settings.favicon || isDisposed) { + return; } + + const faviconUrl = settings.favicon; + const timestamp = Date.now(); + const urlWithCache = `${faviconUrl}${faviconUrl.includes("?") ? "&" : "?"}v=${timestamp}`; + + // 仅处理本组件创建的 link,避免删除 Next/React 管理的 head 节点 + const oldLinks = document.querySelectorAll( + 'link[data-managed-favicon="true"]' + ); + oldLinks.forEach((link) => { + if (link.parentNode) { + link.parentNode.removeChild(link); + } + }); + + const createManagedLink = (rel: string, href: string, type?: string) => { + const link = document.createElement("link"); + link.rel = rel; + link.href = href; + link.setAttribute("data-managed-favicon", "true"); + if (type) { + link.type = type; + } + document.head.appendChild(link); + }; + + createManagedLink("icon", urlWithCache, "image/x-icon"); + createManagedLink("apple-touch-icon", urlWithCache); + createManagedLink("shortcut icon", urlWithCache); }) .catch((error) => { console.error("[Favicon] Error updating favicon:", error); }); + + return () => { + isDisposed = true; + }; }, []); return null; } -