Files
infinite-canvas/web/src/lib/analytics.ts
T

86 lines
2.7 KiB
TypeScript

// 统计分析加载器:默认关闭,可同时启用多家。
// 仅支持 GA4 与百度:两者都只接受 ID,脚本地址由代码固定拼接——
// 不接受任意脚本 URL / 内联 JS,避免「配置项被用来在访客浏览器执行任意代码」。
// 全空时不注入任何脚本、不发任何外部请求。这是开源项目的硬要求:
// fork/自托管者默认零统计,官方站点仅通过环境变量注入自己的 ID(ID 不入库)。
import { ANALYTICS_BAIDU_ID, ANALYTICS_GA4_ID } from "@/constant/runtime-config";
type GtagFn = (...args: unknown[]) => void;
declare global {
interface Window {
dataLayer?: unknown[];
gtag?: GtagFn;
_hmt?: unknown[][];
}
}
let initialized = false;
// 记录实际启用了哪些统计,供路由上报时按需分发。
const active = { ga4: false, baidu: false };
function appendScript(src: string, attrs: Record<string, string> = {}) {
const el = document.createElement("script");
el.async = true;
el.src = src;
for (const [key, value] of Object.entries(attrs)) el.setAttribute(key, value);
document.head.appendChild(el);
return el;
}
function initGa4(id: string) {
window.dataLayer = window.dataLayer || [];
const gtag: GtagFn = (...args) => {
window.dataLayer!.push(args);
};
window.gtag = gtag;
appendScript(`https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(id)}`);
gtag("js", new Date());
// SPA 路由上报交给 trackPageview,这里关闭默认的自动 page_view,避免重复。
gtag("config", id, { send_page_view: false });
active.ga4 = true;
}
function initBaidu(id: string) {
window._hmt = window._hmt || [];
appendScript(`https://hm.baidu.com/hm.js?${encodeURIComponent(id)}`);
active.baidu = true;
}
export function initAnalytics() {
if (initialized || typeof window === "undefined") return;
initialized = true;
// 各家相互独立,逐个判断并启用;任一家出错都不影响其它家与主应用。
if (ANALYTICS_GA4_ID) {
try {
initGa4(ANALYTICS_GA4_ID);
} catch {
/* 忽略 */
}
}
if (ANALYTICS_BAIDU_ID) {
try {
initBaidu(ANALYTICS_BAIDU_ID);
} catch {
/* 忽略 */
}
}
}
// SPA 路由切换时上报页面浏览,分发给所有已启用的统计。
export function trackPageview(path: string) {
try {
if (active.ga4 && window.gtag) {
window.gtag("event", "page_view", { page_path: path, page_location: window.location.href });
}
if (active.baidu && window._hmt) {
window._hmt.push(["_trackPageview", path]);
}
} catch {
/* 忽略 */
}
}