Next.js、Gatsby和静态网站的Cookie同意:开发者集成指南
静态网站同意问题
Next.js、Gatsby和Nuxt.js等现代JavaScript框架在网页构建和交付方式上引入了范式转变。页面在构建时或服务器上预渲染,然后在客户端进行hydration。这为cookie同意带来了独特的挑战:同意横幅必须在任何跟踪脚本执行之前准备就绪,但页面本身可能已经在边缘渲染和缓存。
传统的CMP是为服务器渲染的PHP或简单HTML页面设计的,这类页面的文档从上到下线性加载。在具有代码拆分、懒加载和流式服务器端渲染的框架世界中,这些假设不再成立。在这些环境中正确获取同意需要理解渲染管道。
为什么时机比你想的更重要
在标准HTML页面中,将CMP脚本放在<head>中其他脚本之前很简单。在Next.js App Router或Gatsby中,情况更加复杂:
- 预渲染的HTML先到达:浏览器从CDN或服务器接收完整的HTML。如果任何内联脚本或第三方标签嵌入在该HTML中,它们可能在你的同意逻辑加载之前就执行了。
- Hydration间隙:React hydration在HTML绘制之后发生。如果你的同意组件是React组件,它在hydration完成之前不存在功能状态。在此间隙期间,Google标签或分析脚本可能在没有同意的情况下触发。
- 边缘缓存复杂性:如果你使用ISR(Incremental Static Regeneration)或边缘函数,HTML是缓存的。你无法在没有客户端机制的情况下将依赖同意的逻辑动态注入到缓存的HTML中。
核心原则是:同意必须在脚本级别建立,而非组件级别。一个渲染同意横幅的React组件如果只在hydration之后才变得可交互,那就太晚了。
Next.js App Router集成
Next.js 13+的App Router引入了处理脚本的新方式。以下是同意集成的推荐方法:
步骤1:在根布局中加载CMP脚本
在根layout.tsx中使用带有beforeInteractive策略的Next.js Script组件。这告诉Next.js将脚本注入初始HTML文档中,在hydration开始之前:
beforeInteractive策略至关重要。默认的afterInteractive策略在hydration之后加载脚本,对于同意来说太晚了。使用beforeInteractive,CMP脚本包含在服务器渲染的HTML中,并在页面加载时执行。
步骤2:在Google标签之前设置默认同意
在你的Google Tag Manager或gtag.js代码片段之前,包含一个设置默认同意状态的内联脚本。这确保即使GTM在CMP横幅出现之前加载,它也尊重拒绝的默认值:
这个内联脚本应该放在根布局的<head>中,CMP和GTM脚本之前。在Next.js中,你可以为此目的在布局的<head>元素中使用常规的<script>标签。
步骤3:处理路由变化
在单页应用导航中,CMP脚本加载一次,但路由变化不会触发完整页面重新加载。你的CMP必须在客户端导航中持续存在。FlexyConsent自动处理这个问题——一旦加载,它在所有路由变化中保持活跃,无需重新初始化。
Next.js Pages Router集成
对于仍在使用Pages Router的项目,方法类似但使用_document.tsx代替根布局。将CMP脚本放在自定义Document类的<Head>组件中。beforeInteractive策略在Pages Router中的工作方式相同。
关键区别在于_document.tsx仅在服务器上渲染,因此这里的任何同意逻辑都保证在初始HTML负载中。
Gatsby静态网站集成
Gatsby在构建时生成完全静态的HTML。请求时没有服务器端渲染,这简化了某些方面但使其他方面复杂化:
- 使用
gatsby-ssr.tsx将CMP脚本注入每个页面的<head>中。onRenderBodyAPI允许你向head添加脚本,这些脚本将存在于每个静态HTML文件中。 - 避免懒加载同意的Gatsby插件:一些社区插件将同意包装在仅在hydration后才挂载的React组件中。这会造成前面讨论的时间间隙。
- 将同意默认值放在内联中:在
gatsby-ssr.tsx中使用setHeadComponents添加一个设置默认同意状态的内联脚本。此脚本将在静态HTML中并立即执行。
Gatsby的构建时方法意味着你CDN上的每个HTML文件都将包含同意脚本。这实际上是理想的——没有服务器逻辑会失败或错误缓存。
Nuxt.js注意事项
Nuxt.js(基于Vue)有自己的模式。在Nuxt 3中,使用useHead composable或nuxt.config.ts的app head配置来全局添加CMP脚本。Nuxt支持body: false选项(将脚本放在head中)和用于非阻塞加载的async属性。
对于Nuxt的服务器端渲染模式,同样的原则适用:CMP脚本必须在初始HTML响应中,而不是在挂载后由Vue组件动态注入。
避免布局偏移
同意横幅因引起Cumulative Layout Shift (CLS)而臭名昭著,CLS是影响SEO排名的Core Web Vital。当横幅在页面渲染后弹出时,它会将内容向下推或意外覆盖。
减少同意横幅CLS的策略:
- 使用底部定位的横幅:位于视口底部的横幅不会移动页面内容。这是最CLS友好的方法。
- 预留空间:如果必须使用顶部横幅,在CSS中预留垂直空间,使页面布局在横幅渲染之前就考虑到它。
- 避免加载时的模态覆盖:页面渲染后出现的全屏同意墙会造成可感知的布局不稳定。如果需要墙,将其作为初始页面状态的一部分渲染。
- 在head中同步加载CMP:当CMP作为head中的渲染阻塞脚本加载时,横幅可以作为初始绘制的一部分出现,而不是稍后才弹出。
FlexyConsent的框架无关方法
FlexyConsent被设计为与任何框架——或完全没有框架——配合使用,在脚本级别而非组件级别运行。以下是这为什么重要:
- 单个async脚本标签:
<head>中的一个<script>标签就是所需的全部。无需安装npm包,无需框架特定的包装器,无需构建配置。 - 同意默认值立即触发:脚本的第一个操作就是设置Consent Mode V2默认值,在任何回调或DOM操作之前。这意味着Google标签从第一毫秒起就尊重同意。
- 无DOM依赖:同意逻辑不等待React、Vue或Svelte进行hydration。它独立于框架生命周期运行。
- 适用于SSG、SSR、ISR和CSR:因为它是普通脚本,无论页面是静态生成、服务器渲染、增量再生成还是客户端渲染,它的工作方式完全相同。
开发者提示:正确CMP集成的最简单测试是打开浏览器的Network标签,按Google域名过滤,然后重新加载页面。在控制台中同意默认命令出现之前,不应该有任何Google请求触发。如果有,你的CMP加载得太晚了。
FlexyConsent的免费计划支持无限页面浏览量,适用于Next.js、Gatsby、Nuxt、Astro、SvelteKit、Remix和纯HTML。所有这些的集成都是一样的:一个脚本标签,正确放置。