知识库首页 seo-llm 资料 javascript-seo.md

javascript seo

本地来源:seo-llm/skill/google-seo-skill/references/javascript-seo.md

JavaScript SEO 与渲染优化

1. 服务端渲染策略 (SSR/SSG/ISR)

Next.js 渲染模式选择

rendering_strategy:
  SSG:
    use_when: "内容不频繁变更(博客、文档、着陆页)"
    seo_benefit: "最佳 — 完整 HTML 在构建时生成"
    performance: "最快 — 直接提供静态文件"

  ISR:
    use_when: "内容定期更新(产品页、新闻)"
    seo_benefit: "优秀 — 与 SSG 相同,支持增量更新"
    performance: "快速 — 缓存 + 按需再生成"

  SSR:
    use_when: "个性化内容、实时数据"
    seo_benefit: "良好 — 每次请求生成完整 HTML"
    performance: "取决于服务器响应时间"

  CSR:
    use_when: "仅用于交互式功能(仪表板、编辑器)"
    seo_benefit: "差 — Google 需执行 JS 才能索引"
    performance: "首次加载慢"

Next.js 实现示例

// SSG - 静态生成
export default async function BlogPost({ params }: PageProps) {
  const post = await getPost(params.slug)
  return <Article post={post} />
}

export async function generateStaticParams() {
  const posts = await getAllPosts()
  return posts.map((post) => ({ slug: post.slug }))
}

// ISR - 增量静态再生成
export const revalidate = 3600 // 每小时重新验证

// SSR - 服务端渲染
export const dynamic = 'force-dynamic'

2. 关键内容必须在原始 HTML 中

// ✅ 服务端渲染关键 SEO 内容
// app/blog/[slug]/page.tsx (Server Component)
export default async function BlogPost({ params }: PageProps) {
  const post = await getPost(params.slug)

  return (
    <article>
      {/* 这些内容在原始 HTML 中可见,无需 JS */}
      <h1>{post.title}</h1>
      <p>{post.excerpt}</p>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />

      {/* 结构化数据也在服务端渲染 */}
      <ArticleSchema post={post} />
    </article>
  )
}

// ❌ 不要用客户端组件渲染关键 SEO 内容
'use client'
export default function BlogPost() {
  const [post, setPost] = useState(null)
  useEffect(() => {
    fetch('/api/post').then(r => r.json()).then(setPost)
  }, [])
  // Google 必须执行 JS 才能看到内容!
  return post ? <h1>{post.title}</h1> : <Loading />
}

3. 结构化数据必须服务端渲染

// ✅ 结构化数据在 Server Component 中
export default async function ProductPage({ params }: PageProps) {
  const product = await getProduct(params.id)

  return (
    <>
      {/* JSON-LD 在服务端渲染,Google 可直接读取 */}
      &lt;script
        type=&quot;application/ld+json&quot;
        dangerouslySetInnerHTML={{
          __html: JSON.stringify({
            &#x27;@context&#x27;: &#x27;https://schema.org&#x27;,
            &#x27;@type&#x27;: &#x27;Product&#x27;,
            name: product.name,
            // ...
          }),
        }}
      /&gt;
      <ProductDetail product={product} />
    </>
  )
}

// ❌ 不要在客户端 useEffect 中注入结构化数据

4. Meta 标签在原始 HTML Response 中

// ✅ Next.js App Router - metadata 自动在服务端渲染
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
  const post = await getPost(params.slug)
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: { title: post.title, description: post.excerpt },
  }
}
// Next.js 自动将这些 meta 标签放入原始 HTML <head> 中

// ❌ 不要在客户端动态修改 <title> 或 meta 标签
// document.title = 'New Title' // Google 可能不会读取

5. 导航链接规范

<!-- ✅ 标准的 <a href> 链接 — Google 可爬取 -->
<a href="/about">关于我们</a>

<!-- ✅ Next.js Link 组件 — 渲染为标准 <a> 标签 -->
&lt;Link href=&quot;/blog&quot;&gt;博客</Link>

<!-- ❌ JavaScript 导航 — Google 无法爬取 -->
<div onClick={() => router.push('/about')}>关于我们</div>
<button onClick={() => navigate('/blog')}>博客</button>
<span className="link" data-href="/contact">联系</span>

6. 懒加载图片规范

<!-- ✅ 使用原生 loading="lazy" + src 属性 -->
<img src="/image.jpg" loading="lazy" alt="描述" width="800" height="450" />

<!-- ✅ Next.js Image 自动处理 -->
<Image src="/image.jpg" alt="描述" width={800} height={450} />

<!-- ❌ 仅使用 data-src,Google 可能无法索引图片 -->
<img data-src="/image.jpg" class="lazyload" alt="描述" />

7. Hydration 错误检查

hydration_errors:
  common_causes:
    - "服务端和客户端渲染输出不一致"
    - "使用 Date.now() 或 Math.random() 在渲染中"
    - "访问 window/document 在服务端组件中"
    - "HTML 嵌套不正确(如 <p> 内嵌 <div>)"

  detection:
    - "浏览器控制台检查 hydration 错误"
    - "React DevTools Profiler"

  prevention:
    - "使用 useEffect 处理客户端专属逻辑"
    - "使用 suppressHydrationWarning 仅在必要时"
    - "确保 HTML 结构在服务端和客户端一致"

8. JavaScript Bundle 优化

// 代码分割策略
import dynamic from 'next/dynamic'

// 1. 路由级分割 - Next.js 自动处理
// app/page.tsx, app/blog/page.tsx 自动分割

// 2. 组件级分割 - 重型组件
const HeavyEditor = dynamic(() => import('@/components/Editor'), {
  loading: () => <Skeleton />,
  ssr: false, // 仅客户端渲染的交互组件
})

// 3. 第三方库按需导入
// ❌ import _ from 'lodash' // 导入整个库
// ✅ import debounce from 'lodash/debounce' // 只导入需要的

// 4. Tree-shaking 优化
// package.json 中设置 "sideEffects": false

9. Google JS 渲染机制与限制

Google 对 JavaScript 渲染采用三阶段处理:

google_js_rendering:
  stage_1_crawl: "下载 HTML,提取链接和资源引用"
  stage_2_queue: "将页面放入渲染队列(可能延迟数小时到数天)"
  stage_3_render: "使用 headless Chromium 执行 JS 并索引渲染后的内容"

  limitations:
    - "单页 JS 文件总大小限制 15MB(超出部分不执行)"
    - "渲染超时约 5 秒,复杂 JS 可能未完成"
    - "不支持 hash URL(#/path)— 爬虫不会区分 # 后的内容"
    - "Web Components / Shadow DOM 内容可能无法被索引"
    - "IntersectionObserver 懒加载在 Googlebot 中可能不触发"

  best_practices:
    - "关键内容必须在原始 HTML 或首次渲染中可见"
    - "URL 使用 History API(pushState),绝对不用 hash routing"
    - "静态资源使用长期缓存(Cache-Control: max-age=31536000)+ 文件名 hash"
    - "动态内容返回正确的 HTTP 状态码(404 页面不要返回 200 = soft 404)"
    - "确保 JS/CSS 资源不被 robots.txt 阻止"

10. URL Inspection 验证

使用 Google Search Console 的 URL Inspection 工具验证: - 渲染后的 HTML 是否包含关键内容 - 结构化数据是否正确显示 - meta 标签是否完整 - 截图是否与实际页面一致 - 是否有 JavaScript 错误阻止渲染


相关文档

本文档为站内渲染。原始文件本地路径:saas/source/seo-llm/skill-google-seo-skill-references-javascript-seo-48d022.md(仅本地保留,不入库不部署)