Next.js/React中按钮同意后添加分析脚本不生效的问题
问题解决:Next.js中点击同意后动态加载分析脚本并处理路由跳转跟踪
核心问题分析
你当前的实现存在两个关键问题:
- 直接通过
document.head.innerHTML += document.head.innerHTML + ...操作DOM会重复整个head内容,导致DOM结构混乱,且部分浏览器不会执行这种方式插入的脚本。 - Next.js是单页应用,客户端路由跳转不会重新加载页面,即使脚本插入成功,分析工具的初始化逻辑仅执行一次,无法跟踪后续页面的访问行为。
优化解决方案
我们将状态管理、Next.js官方组件和路由监听结合,实现正确的脚本加载与页面跟踪。
步骤1:重构App.js,统一管理同意状态与脚本加载
import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import Head from 'next/head'; import Script from 'next/script'; import cookie from 'js-cookie'; import CookieBanner from '../components/CookieBanner'; export default function App({ Component, pageProps }) { const [gdprOptIn, setGdprOptIn] = useState(null); const router = useRouter(); // 从Cookie读取初始同意状态 useEffect(() => { const optIn = cookie.get('gdprOptIn') === 'true'; setGdprOptIn(optIn); }, []); // 监听路由变化,发送页面跟踪事件 useEffect(() => { const handleRouteChange = (url) => { if (gdprOptIn && window.gtag) { window.gtag('config', 'G-MY G TAG HERE', { page_path: url, }); } // Hotjar手动触发页面状态更新 if (gdprOptIn && window.hj) { window.hj('stateChange', url); } }; router.events.on('routeChangeComplete', handleRouteChange); return () => router.events.off('routeChangeComplete', handleRouteChange); }, [gdprOptIn, router]); // 处理用户同意/拒绝的回调 const handleGDPRConsent = (optIn) => { cookie.set('gdprOptIn', optIn, { expires: 365 }); setGdprOptIn(optIn); }; return ( <> {gdprOptIn && ( <> <Head> {/* Hotjar初始化脚本 */} <script dangerouslySetInnerHTML={{ __html: ` (function(h,o,t,j,a,r){ h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)}; h._hjSettings={hjid:YOUR_HOTJAR_ID,hjsv:6}; a=o.getElementsByTagName('head')[0]; r=o.createElement('script');r.async=1; r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv; a.appendChild(r); })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv='); `, }} /> {/* Google Analytics初始化 */} <script dangerouslySetInnerHTML={{ __html: ` window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-MY G TAG HERE'); `, }} /> </Head> {/* Google Tag Manager脚本 */} <Script id='ga_tagmanager' strategy='afterInteractive' async src="https://www.googletagmanager.com/gtag/js?id=G-MY G TAG HERE" /> </> )} {/* 仅当未设置同意状态或拒绝时显示横幅 */} {(gdprOptIn === null || !gdprOptIn) && ( <CookieBanner onConsent={handleGDPRConsent} /> )} <Component {...pageProps} /> </> ); }
步骤2:简化CookieBanner组件,移除DOM操作
import Link from "next/link"; import React, { useState } from 'react'; export default function CookieBanner({ onConsent }) { const [showBanner, setBanner] = useState(true); const handleBanner = (setOptIn) => { setBanner(false); // 调用App组件的回调处理同意逻辑 onConsent(setOptIn); }; return ( <div className={`cookie-banner ${!showBanner ? 'hide-banner' : ''}`} id="cookieBanner"> <div className="cookie-card"> <h1>Cookies</h1> <p>我们希望使用分析Cookie,以便了解您如何使用网站并进行改进。</p> <p>我们还使用必要Cookie来记住您是否接受了分析Cookie。<Link href={'/privacy/cookies'}>了解更多</Link></p> <form className="button-wrapper"> <button value={'accept'} type="button" className='btn btn-primary' onClick={() => handleBanner(true)}>全部接受</button> <button value={'reject'} type="button" className='btn btn-primary' onClick={() => handleBanner(false)}>拒绝</button> </form> </div> </div> ); }
关键优化点说明
- 状态统一管理:将GDPR同意状态提升到App组件,避免在子组件中直接操作DOM,符合React的状态管理规范。
- 路由事件监听:通过
router.events监听路由切换完成事件,手动触发分析工具的页面跟踪,解决单页应用的页面访问统计问题。 - 安全加载脚本:使用Next.js官方的
<Script>组件和dangerouslySetInnerHTML,确保脚本正确执行,同时支持加载策略配置(如afterInteractive)。
内容的提问来源于stack exchange,提问作者jonnow
相关产品推荐
相关产品推荐

