Các framework JavaScript phía máy khách

React, Vue, Angular, Svelte và SolidJS có chung một vấn đề SEO — kết xuất phía máy khách tạo ra một khung HTML trống — cùng một cách khắc phục: quản lý phần head kết hợp với chiến lược kết xuất.

React, Vue, Angular, Svelte và SolidJS là các thư viện giao diện người dùng thường gửi một khung HTML gần như trống theo mặc định rồi dựng trang trong trình duyệt bằng JavaScript. Vì vậy, trình thu thập dữ liệu chỉ nhận được một trang trống cho đến khi JavaScript chạy — Google có thể kết xuất trang, nhưng phải qua một hàng đợi riêng; khả năng của các trình thu thập dữ liệu khác như Bing và bot AI kém ổn định hơn nhiều. Cách khắc phục gồm hai phần giống nhau cho cả năm công nghệ: (1) dùng công cụ quản lý phần head để mỗi tuyến có thẻ meta riêng và (2) chọn chiến lược kết xuất như kết xuất trước, SSR hoặc chuyển sang meta-framework tương ứng. Bài tổng quan này giải thích vấn đề và cách khắc phục chung, sau đó dẫn đến hướng dẫn chuyên sâu cho từng framework.

Tóm tắt — React, Vue, Angular, Svelte và SolidJS đều là các thư viện giao diện người dùng thường mặc định dùng kết xuất phía máy khách: chúng gửi một khung HTML gần như trống rồi dựng DOM trong trình duyệt. Vấn đề SEO giống nhau ở cả năm công nghệ: trình thu thập dữ liệu nhận được khung trống, còn nội dung chỉ tồn tại sau khi JavaScript chạy. Google có thể kết xuất những ứng dụng này nhưng phải qua một hàng đợi riêng có độ trễ; Bing và các trình thu thập dữ liệu AI kém ổn định hơn nhiều. Cách khắc phục cũng giống nhau: (1) quản lý phần head (gói theo từng tuyến cho <title>/meta) và (2) chiến lược kết xuất (kết xuất trước, SSR hoặc chuyển sang meta-framework tương ứng). Điểm khác nhau giữa các framework chủ yếu là dùng gói nào và chọn chiến lược nào — được trình bày trong từng hướng dẫn chuyên sâu bên dưới.

Chúng là thư viện chứ không phải framework hoàn chỉnh — đó là gốc rễ vấn đề

Kiến trúc framework rất đa dạng, vì vậy kết xuất phía máy khách không tự động dẫn đến thất bại trong việc lập chỉ mục. Bằng chứng cho nhận định này Primary standard or official documentation supporting the adjacent article claim. Phạm vi: Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Độ tin cậy: cao · Đã xác minh: MDN: Client-side frameworks Kết xuất phía máy chủ hoặc kết xuất trước giúp giảm sự phụ thuộc vào việc trình thu thập dữ liệu chạy JavaScript, nhưng không bảo đảm nội dung sẽ được lập chỉ mục. Bằng chứng cho nhận định này Primary standard or official documentation supporting the adjacent article claim. Phạm vi: Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Độ tin cậy: cao · Đã xác minh: Google: JavaScript SEO basics

React, Vue, Angular, Svelte và SolidJS chủ yếu là thư viện kết xuất giao diện người dùng. Nhiệm vụ của chúng là chuyển dữ liệu thành DOM và đồng bộ DOM khi trạng thái thay đổi. Ngay khi mới cài đặt, DOM được dựng trong trình duyệt — chính điều đó tạo cảm giác nhanh như ứng dụng, đồng thời cũng tạo ra vấn đề SEO.

Bản dựng mặc định gửi một khung HTML có nút gắn kết trống (<div id="root">, <div id="app">, <app-root>) cùng một gói JavaScript. Mọi thứ mà công cụ tìm kiếm quan tâm — tiêu đề, nội dung thân trang, liên kết nội bộ, dữ liệu có cấu trúc — chỉ được gói mã đó chèn vào sau khi tải xuống và chạy. Trước khi JS chạy, chúng chưa tồn tại.

Đó là kết xuất phía máy khách — hành vi ban đầu của thư viện thuần túy trước khi bạn thêm meta-framework hoặc bước kết xuất trước ở thời điểm dựng. Đây không phải lỗi mà là điểm khởi đầu mặc định. Tuy nhiên, “mặc định” không có nghĩa là “vĩnh viễn” hay “phổ quát”: hành vi có thể thay đổi theo phiên bản, CLI hoặc bộ khởi tạo, và hai tuyến trong cùng một ứng dụng có thể dùng hai chế độ kết xuất khác nhau tùy cách xây dựng (chẳng hạn trang tiếp thị được kết xuất trước còn bảng điều khiển vẫn dùng CSR hoàn toàn). Đừng suy đoán hành vi từ tên framework; hãy kiểm tra đầu ra thực tế của từng tuyến trong từng bản dựng.

Vấn đề chung: khung HTML trống

Google nêu rõ rằng họ xử lý ứng dụng JS theo các giai đoạn — thu thập dữ liệu, kết xuất, lập chỉ mục — và rằng “without rendering Google might not see that content” (bản dịch) «nếu không kết xuất, Google có thể không nhìn thấy nội dung đó». Với ứng dụng CSR, toàn bộ nội dung nằm sau bước kết xuất. Điều này dẫn đến ba hệ quả:

  • Kết xuất có độ trễ và thời gian chờ không cố định. Kết xuất là bước riêng trong hàng đợi sau khi thu thập dữ liệu. Google mô tả thu thập dữ liệu, kết xuất và lập chỉ mục là ba giai đoạn khác nhau, nhưng không cam kết một thời gian chờ chung cho mọi trang. Độ trễ thay đổi theo URL và nguồn lực Google có tại thời điểm đó; nội dung chưa tồn tại để lập chỉ mục cho đến khi bước kết xuất thực sự hoàn tất, dù trang đó mất bao lâu.
  • Không thể mặc định các trình thu thập dữ liệu ngoài Google cũng làm được. Bing kết xuất JavaScript thiếu nhất quán, còn phần lớn trình thu thập dữ liệu AI (các bot lấy trang cho LLM và công cụ tìm kiếm AI) chỉ chạy rất ít hoặc không chạy JS. Đây là các công ty có hạ tầng riêng nên tài liệu của Google không thể đại diện cho tất cả. Hãy kiểm tra hành vi hiện tại của từng nhà cung cấp thay vì giả định họ giống Google hoặc giống kết quả thử nghiệm năm trước. Website chỉ dùng CSR có nguy cơ gần như vô hình với những bot không kết xuất, trong khi bot AI đang chiếm tỷ trọng truy cập ngày càng lớn.
  • Thiếu metadata riêng cho từng trang. Một tệp HTML đồng nghĩa mọi tuyến có chung một <title> và một mô tả meta, trừ khi bạn chủ động quản lý phần head theo từng tuyến.

Phần một của cách khắc phục chung: quản lý phần head

Vì SPA chỉ có một tài liệu HTML, bạn cần mã cập nhật phần head của tài liệu khi người dùng (và trình kết xuất của bot) di chuyển giữa các tuyến. Mỗi framework đều có gói tiêu chuẩn hoặc API tích hợp cho việc này:

  • React → react-helmet-async (hoặc API metadata của framework nếu bạn đã chuyển sang Next.js).
  • Vue → @unhead/vue (công cụ nền tảng cho các tiện ích SEO của Nuxt).
  • Angular → các dịch vụ Title và Meta tích hợp sẵn từ @angular/platform-browser — không cần thêm phần phụ thuộc.
  • Svelte → phần tử <svelte:head> tích hợp sẵn.
  • SolidJS → @solidjs/meta (và SolidStart cho hướng triển khai SSR).

Quản lý phần head giúp tạo thẻ meta chính xác, riêng biệt nhưng không tự giải quyết vấn đề khung HTML trống. Các thẻ vẫn chỉ xuất hiện sau khi JS chạy. Vì vậy bạn còn cần phần thứ hai.

Phần hai của cách khắc phục chung: chiến lược kết xuất

Để đưa nội dung thực (và các thẻ trong head) vào HTML trước khi JavaScript chạy, bạn chọn một trong ba phương án. Xếp theo mức độ giảm rủi ro SEO:

  1. Kết xuất trước / tạo trang tĩnh (SSG). Tạo HTML tĩnh cho từng tuyến tại thời điểm dựng. Đây là phương án rủi ro thấp nhất với nội dung không thay đổi theo từng yêu cầu. Mỗi thư viện đều có hướng kết xuất trước (ví dụ tính năng tích hợp của SolidJS, plugin kết xuất trước cho Vue và tính năng kết xuất trước của Angular).
  2. Kết xuất phía máy chủ (SSR). Kết xuất HTML trên máy chủ cho từng yêu cầu rồi hydration trong trình duyệt. Đây là vai trò của các meta-framework: Next.js (React), Nuxt (Vue), Angular SSR (trước đây là Angular Universal), SvelteKit (Svelte) và SolidStart (SolidJS). Với phần lớn website nội dung cần xếp hạng, dùng meta-framework tương ứng là cách khắc phục gọn gàng nhất.
  3. Tiếp tục dùng CSR hoàn toàn nhưng làm cho nội dung có thể thu thập. Cách này đôi khi phù hợp với bề mặt giống ứng dụng nằm sau đăng nhập và không cần xếp hạng, nhưng không nên là lựa chọn mặc định cho nội dung bạn muốn xuất hiện trong tìm kiếm.

Hãy quyết định theo từng tuyến, không phải theo toàn bộ ứng dụng. Trang tiếp thị và bảng điều khiển sau đăng nhập trong cùng một codebase hoàn toàn có thể nằm ở hai phương án khác nhau. Với từng tuyến, hãy hỏi:

  • Đầu ra — nội dung có cần nằm trong phản hồi ban đầu hay xuất hiện sau khi JS chạy cũng được?
  • Độ mới — nội dung có đủ ổn định để dựng một lần (kết xuất trước) hay thay đổi theo từng yêu cầu (SSR)?
  • Cá nhân hóa — nội dung có khác nhau với từng khách truy cập không? Kết xuất trước không giải quyết được trường hợp này; bạn phải chọn giữa SSR và CSR.
  • Chi phí máy chủ — SSR dùng thêm tài nguyên tính toán cho mỗi yêu cầu; kết xuất trước chuyển chi phí đó sang thời điểm dựng.
  • Mức phụ thuộc JS — giá trị sử dụng của tuyến phụ thuộc đến đâu vào việc JavaScript phía máy khách phải chạy?
  • Hành vi khi lỗi — nếu JS lỗi hoặc bị chặn, tuyến có giảm cấp thành nội dung vẫn dùng được hay không còn gì cả?

Cuộc thảo luận để ra quyết định giống nhau với mọi framework: nội dung này có cần xếp hạng hoặc được trích dẫn không? Nếu có, hãy đưa nó vào HTML được kết xuất phía máy chủ hoặc kết xuất trước. Nếu chỉ là giao diện ứng dụng tương tác, CSR vẫn phù hợp.

Google xử lý các ứng dụng này ra sao (và vì sao không nên suy rộng sang bot khác)

Google có thể kết xuất cả năm framework này — họ chạy Chrome không giao diện luôn được cập nhật và thực thi JS. Tuy nhiên, việc này diễn ra trong một hàng đợi trì hoãn; trình kết xuất không lưu trạng thái (không giữ cookie/localStorage, không có service worker, không tương tác — không cuộn hoặc nhấp). Vì vậy, các quy tắc JS-SEO trong bài JavaScript SEO vẫn áp dụng cùng với lựa chọn framework: dùng liên kết <a href> thực, bảo đảm DOM thô và DOM sau kết xuất tương đương, không giấu nội dung sau thao tác và không chèn noindex bằng JS.

Bên ngoài Google, tình hình kém thuận lợi hơn. Khả năng kết xuất JS của Bing thiếu ổn định, còn các trình thu thập dữ liệu AI phần lớn không kết xuất. Với ứng dụng CSR, nội dung có thể tồn tại cho Google (sau một khoảng chờ) nhưng không tồn tại với Bing hay những công cụ AI mà ngày càng nhiều người dùng để tìm kiếm. SSR hoặc kết xuất trước khép lại khoảng trống này cho tất cả chứ không chỉ Google — đây là lý do thuyết phục nhất để không phát hành nội dung chỉ có CSR.

Tổng quan về năm framework

FrameworkMặc địnhQuản lý phần headSSR / meta-frameworkGhi chú SEO
ReactCSRreact-helmet-asyncNext.js (Metadata API)SPA ưu tiên CSR phổ biến nhất; Next.js là cách khắc phục SEO tiêu chuẩn
VueCSR@unhead/vueNuxt (useSeoMeta())Nuxt mặc định dùng SSR; Vue thuần cần kết xuất trước hoặc Nuxt
AngularCSR (SPA)dịch vụ Title/Meta tích hợpAngular SSR (trước đây là Universal)Phù hợp với SPA lớn; Angular hiện đại bổ sung SSR và hydration tăng dần
SvelteCSR (Svelte) / SSR (SvelteKit)<svelte:head>SvelteKit (mặc định SSR)SvelteKit mặc định dùng SSR — lưu ý bẫy tĩnh ssr:false
SolidJSCSR@solidjs/metaSolidStartPhản ứng ở mức chi tiết; SolidStart bổ sung SSR và kết xuất trước

Mô hình luôn nhất quán: mặc định CSR, một gói quản lý head và một chiến lược kết xuất (thường là meta-framework tương ứng). Điểm khác biệt nằm ở tên công cụ và một số góc cạnh dễ gây lỗi.

Bước tiếp theo: hướng dẫn chuyên sâu cho từng framework

Bài tổng quan này là bản đồ. Mỗi framework có hướng dẫn riêng về gói, cấu hình và những điểm dễ mắc lỗi:

  • SEO cho React — vì sao React ưu tiên CSR tạo rủi ro lập chỉ mục, React Router và History API, react-helmet-async, cùng thời điểm nên dùng Next.js.
  • SEO cho Vue — mặc định CSR của Vue 3, createWebHistory(), @unhead/vue, kết xuất trước không cần meta-framework và thời điểm Nuxt là lựa chọn phù hợp.
  • SEO cho Angular — mặc định SPA của Angular, @angular/ssr, các dịch vụ Title và Meta tích hợp, kết xuất trước và hydration tăng dần.
  • SEO cho Svelte — Svelte so với SvelteKit, SSR mặc định trong SvelteKit, <svelte:head>, bẫy adapter-static + ssr: false và ảnh hưởng với bot AI.
  • SEO cho SolidJS — mặc định CSR của SolidJS, @solidjs/meta, SolidStart cho SSR và kết xuất trước, cùng ảnh hưởng của mô hình phản ứng chi tiết đến đầu ra đã kết xuất.

Để tìm hiểu chính các chế độ kết xuất (CSR, SSR, SSG, ISR, hydration, kết xuất động) và những quy tắc rộng hơn về kết xuất/tính tương đương, hãy xem bài tổng quan JavaScript SEO.

Thêm ghi chú chuyên gia

Ghim trích dẫn chuyên gia

Người mới? Hãy tạo hồ sơ chưa có người xác nhận cho họ tại /admin/experts/ → Ghim trích dẫn chuyên gia trước.