JavaScript SEO 基础:渲染、可索引内容与懒加载
现代网站越来越依赖 JavaScript 来渲染内容。问题是:搜索引擎能不能"看到"这些靠 JS 生成的内容?答案是"能,但有代价、有延迟、有不确定性"。理解 Google 的渲染机制,避开客户端渲染的常见陷阱,让关键内容稳定可索引,是每个用框架建站的人都该掌握的技术 SEO 基本功。
Google 的两阶段渲染#
Google 处理网页不是一步完成,而是分两个阶段(常被称为"两波索引")。这一机制在抓取与收录原理中已简述,这里展开:
- 第一阶段:抓取与初始 HTML 解析。Googlebot 抓取 URL,解析返回的原始 HTML,提取其中的文本、链接,并把页面排入渲染队列。这一步几乎实时。
- 第二阶段:渲染。Google 的网页渲染服务(WRS)用一个无头 Chromium 浏览器执行页面的 JavaScript,生成最终的 DOM。只有这一步完成后,JS 注入的内容才进入索引。
三种渲染模式#
同一个页面,内容在什么时候、什么地方被"渲染"成 HTML,决定了它对 SEO 的友好程度。
| 模式 | 内容在哪生成 | 初始 HTML | SEO 友好度 |
|---|---|---|---|
| CSR 客户端渲染 | 用户浏览器执行 JS | 空壳(只有容器) | ⚠️ 依赖渲染,有延迟风险 |
| SSR 服务端渲染 | 服务器每次请求时生成 | 完整内容 | ✅ 友好 |
| SSG 静态生成 | 构建时预先生成 | 完整内容 | ✅ 最友好(兼顾速度) |
CSR:客户端渲染
纯 CSR 的页面,初始 HTML 往往只是这样一个空壳:
<body>
<div id="root"></div> <!-- 空的,内容全靠 JS 注入 -->
<script src="/app.js"></script>
</body>
搜索引擎第一波只看到一个空 <div>,必须等到渲染阶段执行完 app.js 才能看到真实内容。这就是单页应用(SPA)SEO 困难的根源。
SSR:服务端渲染
SSR 在服务器上为每个请求执行框架代码,返回已经填好内容的完整 HTML,浏览器拿到后再"激活"(hydration)接管交互。搜索引擎第一波就能看到全部内容。代价是服务器需要为每次请求做渲染,有运行时开销。
SSG:静态生成
SSG 在构建时就把所有页面渲染成完整的 HTML 文件,部署后就是一堆静态文件,无需服务端运行时。它同时具备 SEO 友好(内容在 HTML 中)、速度极快(可走 CDN)、成本低、攻击面小等优点,是内容型网站的理想选择。
让内容可被索引#
无论用什么框架,核心原则只有一条:关键内容应当在初始 HTML 中就存在,或确保能被可靠渲染。具体注意:
- 关键文本不要只靠 JS 注入:标题、正文、产品信息等核心内容尽量服务端输出。
- meta 标签要在 HTML 中:
title、description、canonical、hreflang、结构化数据等最好在初始 HTML 里,靠 JS 动态写入虽可被读取,但增加不确定性。 - 不要因 User-Agent 区别对待:给搜索引擎和用户返回不同内容属于伪装(cloaking),违反规范。
- 避免渲染依赖用户交互:需要点击、滚动才加载的内容,爬虫可能看不到。
可抓取的链接#
链接是搜索引擎发现页面的主要途径。JavaScript 站点最常见的错误是用脚本事件模拟"链接",导致爬虫无法跟踪。
<!-- 好:真实的 a 标签 + href,爬虫能跟踪 -->
<a href="/articles/zh/technical-seo/javascript-seo.html">JS SEO</a>
<!-- 差:onclick 跳转,没有 href,爬虫无法发现目标 -->
<span onclick="location.href='/page'">点击</span>
<!-- 差:用按钮做导航 -->
<button onclick="goTo('/page')">下一页</button>
规则很简单:导航必须使用带 href 的真实 <a> 标签,指向真实 URL。前端路由(如 React Router)也应基于 <a href> 实现,让 JS 拦截点击做无刷新跳转的同时,保留可被抓取的 href。这一点与URL 结构和移动端导航的要求一致。
懒加载的正确姿势#
懒加载能显著提升加载速度,但实现不当会让内容对爬虫不可见。关键区别在于"加载触发方式":
| 做法 | 对 SEO |
|---|---|
图片用原生 loading="lazy" | ✅ 安全,浏览器与爬虫都能正确处理 |
| 用 IntersectionObserver 懒加载,且内容有真实链接/在 DOM 中 | ✅ 基本安全 |
| 必须用户滚动才加载下一批内容(无分页链接) | ⚠️ 爬虫不滚动,可能看不到 |
| "加载更多"按钮且无对应可抓取 URL | ⚠️ 后续内容难被发现 |
?page=2 且每页有真实链接),让爬虫能逐页发现内容。这部分在「分页、筛选与参数 URL」一篇会专门展开。常见陷阱#
- robots.txt 屏蔽了 JS/CSS:渲染需要这些资源,屏蔽后 Google 无法正确渲染页面(见 robots.txt 配置)。
- 关键内容依赖客户端请求:内容靠 JS 发起 API 请求后才填充,渲染失败或超时就什么都没有。
- 用 # 片段做路由:
example.com/#/page这类哈希路由对收录不友好,应使用 History API 的真实路径。 - meta/canonical 被 JS 覆盖出错:动态写入时逻辑出 bug,可能给所有页面写上相同或错误的 canonical。
- JS 错误中断渲染:一个未捕获的脚本异常可能让整页内容无法生成。
- 渲染超时:脚本过重、请求过慢,渲染在完成前被中断,内容缺失。
如何排查#
判断"搜索引擎到底看到了什么",用这些方法:
- GSC URL 检查工具:查看 Google 渲染后的 HTML 和截图,确认 JS 内容是否被看到——这是最权威的方式。
- 富媒体结果测试 / 移动设备适合性测试:也能查看 Google 渲染后的结果。
- 查看渲染后源码:浏览器里"查看网页源代码"看到的是初始 HTML;开发者工具 Elements 面板看到的是渲染后 DOM。对比两者,能直观看出哪些内容是 JS 注入的。
- 禁用 JavaScript 访问:在浏览器中关闭 JS 再打开页面,看还剩多少内容——剩下的就是初始 HTML 里的部分。
更多细节参见 Google 官方文档:JavaScript SEO 基础知识。
本站为什么用纯静态#
JavaScript SEO 清单#
- 关键内容在初始 HTML 中可见(SSR/SSG 或静态)
- title/description/canonical/结构化数据在 HTML 中
- 导航使用带 href 的真实 a 标签
- 前端路由使用 History API 真实路径,非 # 哈希
- robots.txt 未屏蔽渲染所需的 JS/CSS
- 图片用原生 loading="lazy"
- 无限滚动配套可抓取的分页 URL
- 关键内容不依赖用户点击/滚动才出现
- 不对搜索引擎与用户返回不同内容(无 cloaking)
- 用 GSC URL 检查确认渲染后内容完整
常见问题#
Google 能抓取 JavaScript 生成的内容吗?
能,但有代价。Google 会用无头 Chromium 执行 JavaScript 来渲染页面,所以客户端渲染的内容理论上可以被索引。但渲染需要排队、消耗资源,存在从几小时到几周不等的延迟,而且并非每个搜索引擎、每次抓取都能完整渲染。依赖 JavaScript 才能显示的关键内容,发现和收录都会被推迟,存在不确定性。最稳妥的做法是让核心内容在初始 HTML 中就存在。
单页应用(SPA)对 SEO 不友好吗?
纯客户端渲染的 SPA 对 SEO 有天然劣势:初始 HTML 往往只有一个空容器,内容全靠 JS 注入,搜索引擎需要额外渲染才能看到,且渲染有延迟和不确定性。解决办法不是放弃 SPA,而是给它加上服务端渲染(SSR)或预渲染/静态生成(SSG),让首屏关键内容直接出现在 HTML 中,同时保留客户端交互能力。Next.js、Nuxt 等框架就是为此设计的。
SSR、SSG 和 CSR 有什么区别?
CSR(客户端渲染)由浏览器执行 JS 生成内容,初始 HTML 是空壳;SSR(服务端渲染)在服务器为每次请求生成完整 HTML 再交给浏览器;SSG(静态生成)在构建时就生成好所有页面的完整 HTML,部署为静态文件。从 SEO 角度,SSG 和 SSR 都让内容在初始 HTML 中可见,对搜索引擎最友好;SSG 还兼具最快的速度和最低的服务器成本,适合内容型网站。
懒加载会影响 SEO 吗?
正确实现的懒加载不影响 SEO,还能提升加载速度。但如果用纯 JavaScript 的滚动懒加载(必须用户滚动或交互才加载内容),Googlebot 不会像真人那样滚动,可能看不到这些内容。安全做法是:图片用原生 loading="lazy";列表内容确保有可被抓取的真实链接或在 HTML 中可访问,不要让关键内容只在用户交互后才出现。