/** * NoSsr Component - Lynx 版 MUI NoSsr * 一比一对应 MUI NoSsr * * NoSsr 用于跳过服务端渲染 (SSR),仅在客户端渲染子元素 * * 功能: * - 延迟渲染 (defer mode) * - 占位符支持 (fallback) * - 首次挂载检测 */ // Hooks 类型在 lynx.d.ts 中声明 export interface NoSsrProps { /** 子元素 */ children?: any /** 是否延迟渲染 (使用 requestAnimationFrame/setTimeout) */ defer?: boolean /** 服务端渲染时的占位符 */ fallback?: any } /** * useEnhancedEffect - 在客户端使用 useLayoutEffect,服务端使用 useEffect * 对应 MUI 的 useEnhancedEffect */ const useEnhancedEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect /** * NoSsr 组件 - 完整实现 * * 用法: * ```tsx * // 基础用法 * * * * * // 延迟渲染 - 使用 requestAnimationFrame * * * * * // 带占位符 * }> * * * ``` */ export function NoSsr(props: NoSsrProps) { const { children, defer = false, fallback = null } = props // 挂载状态 const [mounted, setMounted] = useState(false) // 使用 useEnhancedEffect 确保在客户端执行 useEnhancedEffect(() => { if (!defer) { // 立即挂载 setMounted(true) } }, [defer]) // defer 模式:延迟到下一帧 useEffect(() => { if (defer) { // 使用 requestAnimationFrame 或 setTimeout 延迟渲染 // 这允许浏览器先完成当前帧的渲染 let rafId: number let timeoutId: ReturnType if (typeof requestAnimationFrame !== 'undefined') { rafId = requestAnimationFrame(() => { setMounted(true) }) } else { timeoutId = setTimeout(() => { setMounted(true) }, 0) } return () => { if (rafId) { cancelAnimationFrame(rafId) } if (timeoutId) { clearTimeout(timeoutId) } } } return undefined }, [defer]) // 未挂载时显示占位符 if (!mounted) { return fallback } // 已挂载,渲染子元素 return children } export default NoSsr