import { useEffect, useRef, type CSSProperties } from "react"; import { loadAdSense, pushAdSense } from "../loader.js"; export interface AdSlotProps { /** 广告单元 ID(data-ad-slot) */ adSlot: string; /** 发布商 ID(data-ad-client,形如 ca-pub-xxxxxxxx) */ adClient: string; /** data-ad-format,默认 auto */ format?: string | undefined; /** data-ad-layout */ layout?: string | undefined; /** data-ad-layout-key */ layoutKey?: string | undefined; /** data-full-width-responsive,未传时不输出该属性 */ fullWidthResponsive?: boolean | undefined; className?: string | undefined; style?: CSSProperties | undefined; } /** * AdSense 广告位组件:渲染 , * 挂载后按需加载官方脚本并 push 渲染命令。 * * 注意:属性名刻意避开 Astro 的 `slot` / `client:*` 保留字, * 在 Astro island 中可直接透传。 */ export function AdSlot({ adSlot, adClient, format = "auto", layout, layoutKey, fullWidthResponsive, className, style, }: AdSlotProps) { const pushedKeyRef = useRef(null); // 按 adClient#adSlot 去重 push;StrictMode 双调用与依赖重跑都安全 useEffect(() => { let cancelled = false; const key = `${adClient}#${adSlot}`; loadAdSense(adClient) .then(() => { if (cancelled || pushedKeyRef.current === key) { return; } pushedKeyRef.current = key; pushAdSense(); }) .catch((err: unknown) => { console.error("[adsense]", err); }); return () => { cancelled = true; }; }, [adClient, adSlot]); return ( ); }