Cookie Consent cho Next.js, Gatsby và Trang Tĩnh: Hướng Dẫn Tích Hợp cho Lập Trình Viên
Vấn Đề Consent của Trang Tĩnh
Các framework JavaScript hiện đại như Next.js, Gatsby và Nuxt.js đã tạo ra một sự thay đổi mô hình trong cách xây dựng và phân phối trang web. Các trang được render trước tại thời điểm build hoặc trên server, sau đó được hydrate trên client. Điều này tạo ra một thách thức độc đáo cho cookie consent: banner consent phải sẵn sàng trước khi bất kỳ script theo dõi nào thực thi, nhưng bản thân trang có thể đã được render và cache tại edge.
Các CMP truyền thống được thiết kế cho các trang PHP render phía server hoặc trang HTML đơn giản, nơi tài liệu tải tuyến tính từ trên xuống dưới. Trong thế giới framework với code splitting, lazy loading và streaming server-side rendering, các giả định bị phá vỡ. Để có consent đúng trong các môi trường này đòi hỏi phải hiểu pipeline rendering.
Tại Sao Thời Gian Quan Trọng Hơn Bạn Nghĩ
Trong một trang HTML tiêu chuẩn, đặt script CMP trong <head> trước các script khác rất đơn giản. Trong Next.js App Router hoặc Gatsby, tình huống phức tạp hơn:
- HTML render trước đến trước: Trình duyệt nhận HTML hoàn chỉnh từ CDN hoặc server. Nếu bất kỳ inline script hoặc tag bên thứ ba nào được nhúng trong HTML đó, chúng có thể thực thi trước khi logic consent của bạn tải.
- Khoảng trống hydration: React hydration xảy ra sau khi HTML được paint. Nếu component consent của bạn là một React component, nó không tồn tại ở trạng thái hoạt động cho đến khi hydration hoàn tất. Trong khoảng trống này, các tag Google hoặc script analytics có thể kích hoạt mà không có consent.
- Phức tạp edge caching: Nếu bạn sử dụng ISR (Incremental Static Regeneration) hoặc edge function, HTML được cache. Bạn không thể inject động logic phụ thuộc consent vào HTML đã cache mà không có cơ chế phía client.
Nguyên tắc cốt lõi là: consent phải được thiết lập ở cấp script, không phải cấp component. Một React component render banner consent là quá muộn nếu nó chỉ trở nên tương tác sau hydration.
Tích Hợp Next.js App Router
Next.js 13+ với App Router giới thiệu cách mới để xử lý script. Đây là phương pháp được khuyến nghị cho tích hợp consent:
Bước 1: Tải Script CMP trong Root Layout
Sử dụng component Script của Next.js với chiến lược beforeInteractive trong layout.tsx gốc của bạn. Điều này cho Next.js biết inject script vào tài liệu HTML ban đầu, trước khi hydration bắt đầu:
Chiến lược beforeInteractive rất quan trọng. Chiến lược mặc định afterInteractive tải script sau hydration, quá muộn cho consent. Với beforeInteractive, script CMP được bao gồm trong HTML render từ server và thực thi khi trang tải.
Bước 2: Đặt Consent Mặc Định Trước Google Tag
Trước đoạn mã Google Tag Manager hoặc gtag.js của bạn, bao gồm một inline script đặt trạng thái consent mặc định. Điều này đảm bảo ngay cả khi GTM tải trước khi banner CMP xuất hiện, nó tôn trọng các giá trị mặc định bị từ chối:
Inline script này nên được đặt trong <head> của root layout, trước các script CMP và GTM. Trong Next.js, bạn có thể sử dụng tag <script> thông thường bên trong phần tử <head> của layout cho mục đích này.
Bước 3: Xử Lý Thay Đổi Route
Trong điều hướng ứng dụng trang đơn, script CMP tải một lần nhưng thay đổi route không kích hoạt tải lại toàn trang. CMP của bạn phải duy trì qua các điều hướng phía client. FlexyConsent xử lý điều này tự động — một khi đã tải, nó vẫn hoạt động qua tất cả thay đổi route mà không cần khởi tạo lại.
Tích Hợp Next.js Pages Router
Với các dự án vẫn sử dụng Pages Router, phương pháp tương tự nhưng sử dụng _document.tsx thay vì root layout. Đặt script CMP trong component <Head> của lớp Document tùy chỉnh. Chiến lược beforeInteractive hoạt động tương tự trong Pages Router.
Sự khác biệt chính là _document.tsx chỉ render trên server, vì vậy bất kỳ logic consent nào ở đây được đảm bảo nằm trong payload HTML ban đầu.
Tích Hợp Gatsby Static Site
Gatsby tạo HTML hoàn toàn tĩnh tại thời điểm build. Không có server-side rendering tại thời điểm request, điều này đơn giản hóa một số khía cạnh nhưng phức tạp hóa các khía cạnh khác:
- Sử dụng
gatsby-ssr.tsxđể inject script CMP vào<head>của mọi trang. APIonRenderBodycho phép bạn thêm script vào head sẽ có mặt trong mọi file HTML tĩnh. - Tránh các plugin Gatsby lazy-load consent: Một số plugin cộng đồng bọc consent trong React component chỉ mount sau hydration. Điều này tạo ra khoảng trống thời gian đã thảo luận trước đó.
- Đặt consent mặc định inline: Sử dụng
setHeadComponentstronggatsby-ssr.tsxđể thêm inline script đặt trạng thái consent mặc định. Script này sẽ nằm trong HTML tĩnh và thực thi ngay lập tức.
Phương pháp build-time của Gatsby có nghĩa là mọi file HTML trên CDN của bạn sẽ bao gồm script consent. Điều này thực sự lý tưởng — không có logic server nào thất bại hoặc cache sai.
Lưu Ý Nuxt.js
Nuxt.js (dựa trên Vue) có các pattern riêng. Trong Nuxt 3, sử dụng composable useHead hoặc cấu hình app head trong nuxt.config.ts để thêm script CMP toàn cục. Nuxt hỗ trợ tùy chọn body: false (đặt script trong head) và thuộc tính async để tải không chặn.
Với chế độ server-side rendering của Nuxt, nguyên tắc tương tự áp dụng: script CMP phải nằm trong phản hồi HTML ban đầu, không được inject động bởi Vue component sau khi mount.
Tránh Layout Shift
Banner consent nổi tiếng gây ra Cumulative Layout Shift (CLS), một Core Web Vital ảnh hưởng đến xếp hạng SEO. Khi banner xuất hiện sau khi trang render, nó đẩy nội dung xuống hoặc phủ lên bất ngờ.
Chiến lược giảm thiểu CLS từ banner consent:
- Sử dụng banner ở dưới cùng: Banner ở cuối viewport không dịch chuyển nội dung trang. Đây là cách tiếp cận thân thiện với CLS nhất.
- Dành chỗ trước: Nếu bạn phải sử dụng banner trên cùng, dành không gian dọc trong CSS để layout trang tính đến banner trước khi nó render.
- Tránh modal overlay khi tải: Tường consent toàn màn hình xuất hiện sau khi trang render gây ra sự không ổn định layout cảm nhận được. Nếu bạn cần tường, render nó như một phần của trạng thái trang ban đầu.
- Tải CMP đồng bộ trong head: Khi CMP được tải như script chặn render trong head, banner có thể xuất hiện như một phần của lần paint đầu tiên thay vì xuất hiện muộn.
Phương Pháp Không Phụ Thuộc Framework của FlexyConsent
FlexyConsent được thiết kế để hoạt động với bất kỳ framework nào — hoặc hoàn toàn không có framework — bằng cách hoạt động ở cấp script thay vì cấp component. Đây là lý do điều này quan trọng:
- Một tag script async duy nhất: Một tag
<script>trong<head>là tất cả những gì cần thiết. Không cần cài đặt npm package, không wrapper dành riêng cho framework, không cấu hình build. - Consent mặc định kích hoạt ngay lập tức: Script đặt Consent Mode V2 mặc định là hành động đầu tiên, trước bất kỳ callback hoặc thao tác DOM nào. Điều này có nghĩa là Google tag tôn trọng consent từ mili giây đầu tiên.
- Không phụ thuộc DOM: Logic consent không chờ React, Vue hoặc Svelte hydrate. Nó hoạt động độc lập với vòng đời framework.
- Hoạt động với SSG, SSR, ISR và CSR: Vì là script thuần, nó hoạt động giống hệt nhau dù trang được tạo tĩnh, render từ server, tái tạo tăng dần hay render phía client.
Mẹo cho lập trình viên: Bài test đơn giản nhất cho tích hợp CMP đúng là mở tab Network của trình duyệt, lọc theo domain Google và tải lại trang. Không có request Google nào nên kích hoạt trước khi lệnh consent mặc định xuất hiện trong console. Nếu có, CMP của bạn đang tải quá muộn.
Gói miễn phí của FlexyConsent hỗ trợ không giới hạn pageview và hoạt động với Next.js, Gatsby, Nuxt, Astro, SvelteKit, Remix và HTML thuần. Tích hợp giống nhau trên tất cả: một tag script, đặt đúng chỗ.