Google 搜索是唯一默认会跑 JavaScript 的主流搜索引擎(用 evergreen Chromium,常青版 Chromium)。其它搜索引擎(Bing 部分支持、DuckDuckGo / Yandex / 多数小众引擎)不跑 JS — SSR 服务端渲染仍是最安全的选择。
Google 处理 JavaScript 站点的核心机制:WRS(Web Rendering Service,网页渲染服务) + render queue(渲染队列)。
1. Google 处理 JavaScript 的 3 阶段 + 渲染队列
URL
↓
[crawl queue 抓取队列] → Googlebot 取 URL → 检查 robots.txt
↓
fetch HTML → parse 解析 → 抽取链接回 crawl queue
↓
[render queue 渲染队列] → WRS(无头 Chromium)跑 JS → 渲染后 HTML
↓
parse 解析渲染后的 HTML → 抽链接回 crawl queue + 用于 indexing 索引
↓
[index 索引]
关键不变量:
- 所有 HTTP
200页面都进 render queue,无论是否含 JS(Google 自己判断) - 非 200(如 404)可能跳过渲染
- 渲染可能等几秒到更久 — 由 Google 资源决定
- WRS 用当前 evergreen Chromium,跟最新 Chrome 同步
- 被 robots.txt block 阻止的资源 → 不会被 fetch 抓取 → 渲染缺资源 → Google 可能看不到内容
反直觉:渲染队列是异步的。如果你 hash 改了页面但渲染队列堵着,Google 看到的可能是几天前的快照。
2. 15 条 JavaScript SEO 最佳实践(必看)
按重要性排序。
2.1 内容相关
| # | 实践 | 关键点 |
|---|---|---|
| 1 | 仍用 SSR(服务端渲染)/ 预渲染 | 给用户和抓取器都更快,非 Google 抓取器也能看到 |
| 2 | 唯一描述性 <title> + meta description |
JS 设置 OK,但确保渲染完成时这俩存在 |
| 3 | 用 <a href> 而不是 onclick |
Google 只从 <a href> 抓链接 |
| 4 | History API 而不是 URL fragment(片段) | #/products 这种 fragment 路由 Googlebot 不可靠(AJAX crawl scheme 2015 已废弃) |
| 5 | 加 结构化数据 用 JSON-LD,JS 注入也 OK | 详见 Structured Data 完全总览 (框架 + 34 个 rich result type 一键查 + JSON-LD/Microdata/RDFa 选型) |
| 6 | Web Components 用 <slot> 显示 light DOM(明亮 DOM) |
否则 light DOM 内容不进渲染 HTML → 不被索引 |
| 7 | 懒加载用 viewport(视口)检测,不是用户 scroll(滚动) | Google 不滚动你页面 |
2.2 错误处理
| # | 实践 | 关键点 |
|---|---|---|
| 8 | 用准确 HTTP 状态码 | 404 表示 not found / 401 表示 login wall 登录墙 / 301 表示 moved 已迁移 |
| 9 | SPA 单页应用避免 soft 404(软 404) | 客户端路由经常发 200 当作 not-found → 假 200 会被索引。改用 JS 重定向到真 404 URL,或注入 <meta name="robots" content="noindex"> |
| 10 | Robots meta 标签用 JS 注入要小心 | Google 看到 noindex 可能跳过渲染 → JS 想去掉 noindex 不可靠;如果想被索引,原始 HTML 里别有 noindex |
2.3 性能 / 缓存
| # | 实践 | 关键点 |
|---|---|---|
| 11 | 长寿命缓存 + 内容指纹(content fingerprinting) | WRS 可能忽略 cache header,用 main.2bb85551.js 这种 hash 文件名避坑 |
| 12 | 特性检测(feature detection) + polyfill 补丁 | 检测 API 不支持,提供 fallback 退路;某些(如 WebGL)Google 不支持,要 SSR 那部分 |
| 13 | 用 HTTP,不依赖 WebSockets / WebRTC | Googlebot 只发 HTTP,其它连接类型用不了 |
2.4 规范化(Canonical)/ 机器人(Robots)
| # | 实践 | 关键点 |
|---|---|---|
| 14 | rel="canonical" 优先放 HTML |
实在要 JS 注入 OK,但只能保留 1 个 canonical;不要 JS 改成跟原 HTML 不同的值 |
| 15 | Camera / Geo / Notification 等权限请求不要强求 | Googlebot 拒绝权限请求;给个不要权限也能用的路径 |
3. SPA(单页应用)必坑 6 大
| 坑 | 症状 | 修法 |
|---|---|---|
| soft 404 软 404 | 客户端路由 not-found 返 200 → 假错页被索引 |
JS 重定向到真 404 URL,或注入 <meta robots="noindex"> |
fragment 路由(#/products) |
Googlebot 抓不到 fragment 后面的内容 | History API:window.history.pushState({}, '', '/products') |
| Local Storage / Session Storage 失效 | 跨 page load 数据丢 | WRS 每次 page load 都清空 Local / Session Storage 和 Cookie。不能依赖持久化数据 serve 内容 |
| WebSocket / WebRTC 拉数据 | Googlebot 拿不到 | 给 HTTP fallback 退路 |
| JS 改 canonical | 多个 canonical 冲突 → 行为不可预测 | 同一页只能 1 个 canonical,JS 注入前先检查 |
| JS paywall(付费墙)假实现 | 全文藏 HTML 里 + JS 隐藏 | 实际限制访问要服务端做。JS 隐藏只是装样子 |
4. 诊断清单 11 步
怀疑 JavaScript 阻碍索引时:
- 跑 Rich Results Test / URL Inspection Tool — 看加载的资源 / JS 控制台 / 异常 / 渲染后 DOM。Search Console 的 Crawl Stats Report(抓取统计报告)监控 Googlebot + WRS 活动
- 防 soft 404 — 见 §3 第 1 条
- 预期 Googlebot 拒绝权限请求 — 不强求 Camera / Geo / Notification
- 不用 URL fragment 加载不同内容 — 用 History API
- 不依赖数据持久化 — WRS 每次清 Local / Session / Cookie
- content fingerprinting 内容指纹 —
main.<hash>.js防 cache 缓存旧资源 - 特性检测 + 关键 API 的 fallback / polyfill
- HTTP 连接才行 — 不用 WebSocket / WebRTC 抓主内容
- Web Components 渲染正确 —
<slot>用了吗?Rich Results Test 看渲染后 HTML 内容齐不齐 - JS paywall 实现方式 — 完整内容不要在 HTML 里
- 修完再测一遍 — 拿 Rich Results Test / URL Inspection 复验。绿勾通过
收集生产环境的 JS 错误:推荐打 global onerror handler(全局错误处理器),把 Googlebot 和真实用户的 JS 错误都 log 日志到远程,定位渲染失败的原因。
window.addEventListener('error', function(e) {
const errorText = [
e.message,
'URL: ' + e.filename,
'Line: ' + e.lineno + ', Column: ' + e.colno,
'Stack: ' + (e.error && e.error.stack || '(no stack trace)')
].join('\n');
// POST 到自己服务器
fetch('/logError', { method: 'POST', body: errorText });
});
5. Dynamic Rendering(动态渲染)是历史变通方案(2026 不推荐)
结论:别用 dynamic rendering。已经从推荐方案降级,用 SSR / 静态渲染 / hydration 替代。
5.1 它干什么
请求 → 检测 user-agent
├─ Googlebot / 不支持 JS 的抓取器 → 路由到 rendering server 渲染服务器 → 返回静态 HTML
└─ 普通浏览器 → 返回 client-rendered 客户端渲染版本
5.2 为什么弃用
- 增加服务器复杂度(双轨服务)
- 维护成本高
- SSR / hydration 同样解决问题且更简单
- 真"抓取器列表"难维护,新 bot 出来你没适配
5.3 它不算 cloaking(掩饰违规)的条件
只要 dynamic rendering 给两边的内容相似(server-rendered 服务端渲染版 ≈ client-rendered 客户端渲染版),Googlebot 不视为 cloaking。
真 cloaking(违反 Google SEO 入门 + Search 工作原理 + Search Essentials §7 spam 政策)的例子:给用户看猫,给抓取器看狗。
5.4 现代替代方案
| 方案 | 适用 |
|---|---|
| SSR(Server-Side Rendering 服务端渲染) | 默认推荐。Next.js / Nuxt / Astro 等 |
| 静态渲染(Static rendering) | 内容不常变 → 构建时生成 HTML |
| Hydration(注水) | SSR + 后续客户端接管交互 |
| 预渲染(Pre-rendering,类 SSR) | 小站省事 |
6. Lazy Loading(懒加载)正确做法
6.1 核心原则
懒加载必须用 viewport(视口)检测,不是 scroll(滚动)触发。Google 不滚动你的页面。
6.2 3 个实现方式
| 方式 | 何时用 |
|---|---|
浏览器内置 loading="lazy"(<img> / <iframe>) |
现代浏览器原生支持,最简单 |
| IntersectionObserver API + polyfill | 自定义逻辑、视频懒加载 |
| JS 库(react-lazyload 等) | 框架集成 |
6.3 不要的
- 不要对首屏可见内容用懒加载(慢且毫无意义)
- 不要靠 scroll(滚动)事件触发(Google 不 scroll)
6.4 Infinite scroll(无限滚动)的正确实现
无限滚动跟 SEO 天然矛盾,要 paginated 分页化:
- 每个 chunk 块一个持久唯一 URL(
/articles?page=12) - 同一 URL 每次加载内容一致(避免
?date=yesterday这种相对参数) - 顺序链接各 page URL(让抓取器能发现)
- 用户滚动到新 chunk 时,History API 更新 URL(让用户能 refresh / share / link)
- 详见 Ecommerce SEO 完全指南 (6 Surface / 4 Launch 策略 / Merchant Center / 6 SD 类型 / Pagination 3 模式 / Review 13 准则) 的 pagination 章节
6.5 验证
Search Console 的 URL Inspection Tool → Live URL → 看渲染后 HTML:
<img src>/<video src>真实 URL 都在 → 成功- 全是 placeholder URL(占位 URL)→ 失败,Google 没看到真图
7. 关键速查表
| 想做 | 怎么做 | 警告 |
|---|---|---|
| 设页面 title | JS 设置 OK | 确保渲染完成时 title 存在 |
| 加结构化数据 | JS 注入 JSON-LD | 详见 Structured Data 完全总览 (框架 + 34 个 rich result type 一键查 + JSON-LD/Microdata/RDFa 选型) |
| 设 canonical | 优先 HTML;JS 注入 OK | 同页只能 1 个,不要冲突 |
| 设 robots meta | 优先 HTML | noindex 可能跳过渲染 → JS 改不掉 |
| 路由(SPA) | History API | 不要 URL fragment(#/foo) |
| 处理 404 | JS 重定向到真 404 URL,或注入 noindex | 客户端假 200 会被索引 |
| 加图懒加载 | viewport(视口)检测 | 不要 scroll(滚动)触发 |
| 实现无限滚动 | 分页 URL + History API 更新 | 每 chunk 块独立持久 URL |
| 检测 API 支持 | 特性检测 + polyfill | WebGL / WebSocket 等 Google 不支持 |
| 拿数据 | HTTP fetch | 不要 WebSocket / WebRTC 拉主内容 |
| 跨页面持久化 | 服务端 | WRS 每次清 Local / Session / Cookie |
| 加缓存控制 | 内容指纹(content fingerprinting) | WRS 可能忽略 cache header |
| Web Components | <slot> 显示 light DOM(明亮 DOM) |
shadow DOM 影子 DOM only 不被索引 |
| Paywall 付费墙 | 服务端控制完整内容 | JS 隐藏不算限制 |
8. 跟其它 wiki 的关系
| 想知道 | 去 |
|---|---|
| Crawling 抓取整体机制 / Googlebot 详情 | Googlebot 抓取机制与索引控制完全指南 (URL/Links/Mobile-first/Meta/Testing/AMP) |
| canonical 完整玩法 | Canonical / 重定向 / 网站迁移完全指南 (含 HTTP 状态码全表 + 503 临时停业 playbook) |
| robots.txt + robots meta | 抓取与索引控制完全指南 (robots.txt / noindex / X-Robots-Tag / data-nosnippet / rel) |
| 结构化数据 33 种 | Structured Data 完全总览 (框架 + 34 个 rich result type 一键查 + JSON-LD/Microdata/RDFa 选型) |
| pagination 完整实践 | Ecommerce SEO 完全指南 (6 Surface / 4 Launch 策略 / Merchant Center / 6 SD 类型 / Pagination 3 模式 / Review 13 准则) |
| Search Console 监控 | Search Console + Analytics + Trends 三件套用法 (流量诊断 / 关键词调研 / Looker Studio) |
| SEO 全景 / Search 原理 | Google SEO 入门 + Search 工作原理 + Search Essentials |
来源与关联资料
- https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics?hl=en
- https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics?hl=zh-cn
- https://developers.google.com/search/docs/crawling-indexing/javascript/dynamic-rendering?hl=en
- https://developers.google.com/search/docs/crawling-indexing/javascript/dynamic-rendering?hl=zh-cn
- https://developers.google.com/search/docs/crawling-indexing/javascript/fix-search-javascript?hl=en
- https://developers.google.com/search/docs/crawling-indexing/javascript/fix-search-javascript?hl=zh-cn
- https://developers.google.com/search/docs/crawling-indexing/javascript/lazy-loading?hl=en
- https://developers.google.com/search/docs/crawling-indexing/javascript/lazy-loading?hl=zh-cn
- Google SEO 入门 + Search 工作原理 + Search Essentials
- Googlebot 抓取机制与索引控制完全指南 (URL/Links/Mobile-first/Meta/Testing/AMP)
- Canonical / 重定向 / 网站迁移完全指南 (含 HTTP 状态码全表 + 503 临时停业 playbook)
- 抓取与索引控制完全指南 (robots.txt / noindex / X-Robots-Tag / data-nosnippet / rel)