Vue3项目文章不被收录怎么办

Vue3项目文章不被收录怎么办

问题发现:搜索引擎眼里的我的文章页是什么

博客文章页面是 Vue 渲染的,一直怀疑收录不好但没实际验证过。这次我直接模拟 Googlebot 抓自己的文章页:

curl -A "Googlebot" https://stewie.fun/posts/daily-quiz-2026-09-01

拿到的 HTML 里面是这样的:

<body>
  <h1 style="position:absolute;...">Stewie 的前端实验室...</h1>
  <div id="app"></div>
  <script type="module" src="/assets/index-CYJQMOWH.js"></script>
</body>

整个页面就 6.4KB,正文一个字都没有——全部内容要等浏览器下载 JS、执行 Vue、调 API、渲染 DOM 之后才出现。Google 理论上能跑 JS,但渲染队列排期长、渲染预算有限;Bing 对 JS 的支持一直是半吊子;百度更弱,基本只看初始 HTML。结果就是:文章页在百度和 Bing 眼里约等于不存在。

一个容易忽略的坑:隐藏 H1 救不了内容

我之前在源码里塞了个视觉隐藏的 H1,想着至少让 Bing 站长工具能读到 H1。事实证明这只能骗过"检测 H1 是否存在"这类浅层检查,救不了内容收录——正文、内链、结构化数据全都不在源码里,搜索引擎无从索引。

三条路的取舍:Nuxt、预渲染、后端渲染

方案一:重构成 Nuxt 3(SSR/SSG)。最正统,但对我是下下签:整个 Vue3 + TS + Vite 项目迁移,构建链路、路由、部署全换,还要长期维护一个 Node 渲染服务。我的博客本身就是 Vue3 技术栈的展示品,换框架太麻烦了。

方案二:构建时预渲染。只适合页面数量固定的官网。我的文章天天在加,每发一篇就得重新 build,构建产物里永远缺最新的——直接排除。

方案三:后端动态渲染(Dynamic Rendering)。让后端在「爬虫请求」时返回完整 HTML,「用户请求」照旧走 SPA。这是 Google 官方认可的手段,红线只有一条:给爬虫和给用户看的内容必须同源等价——同一篇文章数据、同一份正文,只是省了 JS 渲染步骤。只要不往爬虫版塞正文里没有的东西,就不是 cloaking(隐蔽伪装),不会被惩罚。

我的后端是 Spring Boot,文章正文本来就以 HTML 字符串存在数据库里(前端拿去 v-html 渲染的同一份数据),后端直接吐 HTML 几乎是白捡的——最终选它。

实现架构:Nginx 按 UA 分流

整体链路:

爬虫请求 /post/{slug}
  → Nginx map $http_user_agent 命中爬虫名单
  → 内部改写到 Spring Boot(Thymeleaf 渲染)
  → 返回完整 HTML(meta + JSON-LD + 正文 + 评论区)

人类请求同一 URL
  → UA 不匹配
  → try_files /index.html
  → SPA 接管,体验与之前完全一致

Nginx 侧几个关键设计

  • 爬虫名单用 map 管理:Googlebot、bingbot、Baiduspider、Sogou、360、Bytespider 之外,把 AI 爬虫也放进来了——GPTBot、ClaudeBot、anthropic-ai、PerplexityBot、CCBot、Applebot、Amazonbot、meta-externalagent。这些是 AEO/GEO 的入口,AI 引擎抓不到内容就不会引用你。
  • 内部改写用 error_page 418 手法而不是 if + rewrite:命中爬虫后先返回 418 再 error_page 转内部 location,配合 internal 指令保证外部无法直接访问渲染端点——不然有人伪造 Googlebot UA 就能绕过前端刷接口。
  • proxy_cache 按 URI+UA 做 key,10 分钟,另外配置 proxy_cache_valid 404 30s——否则文章刚发布的瞬间爬虫来了一次 404,这个 404 会在缓存里躺满整个周期。

Spring Boot 侧

一个 Controller + 一个 Thymeleaf 模板就完事:

@GetMapping("/post/{slug}")
public ResponseEntity<String> seoPost(@PathVariable String slug) {
    Post post = postService.getBySlug(slug);
    if (post == null || post.getStatus() != 1) {
        response.setStatus(404);   // 手动 404,别抛异常
        return render404();
    }
    return ResponseEntity.ok()
        .contentType(MediaType.TEXT_HTML).charset(UTF_8)
        .body(renderTemplate(post));
}

两个细节:404 要手动 setStatus,抛异常会被全局异常处理器兜成 200 + JSON 的"软 404",搜索引擎最烦这个;模板里 JSON-LD 用注入的 ObjectMapper 序列化,防止正文里的引号打碎结构化数据。

模板输出完整文档:title/description/canonical/og/twitter 卡片 + BlogPosting JSON-LD + 封面标题元信息 + 正文(th:utext 直注数据库里的 HTML)+ 服务端渲染的评论区 + 热门文章内链。内链对爬虫发现旧文章很有价值——它顺着 5 个链接就把站内相关文章全爬了。

上线验证:数字说话

部署后用同一套 UA 复测:

Googlebot   → 200, 16419B  ✅ 完整正文 + JSON-LD
bingbot     → 200, 16419B  ✅
Baiduspider → 200, 16419B  ✅
GPTBot      → 200, 16419B  ✅
ClaudeBot   → 200, 16419B  ✅
PerplexityBot → 200, 16419B  ✅
Chrome(真人)→ 200, 6879B  SPA 空壳,照旧走前端

爬虫页面的 JSON-LD 校验:BlogPosting 类型、headline、datePublished、author 全部就位;正文 9 个 h2 全在源码里;canonical 指向标准 URL;不存在的 slug 返回真实 404。人类侧页面体积几乎没变,路由、点赞、评论、TOC 交互零改动。

对照上线前:同一个 URL,爬虫拿到的是 6427B 的空壳,现在拿到 16419B 的完整内容——这就是从"搜索引擎眼里不存在"到"正常网页"的差距。

不只有 SEO:AEO 和 GEO 顺路解决

2026 年个人网站的可见性是三件事:SEO 让人搜到你,AEO(Answer Engine Optimization)让 AI 搜索在答案里引用你,GEO(Generative Engine Optimization)让 ChatGPT/Perplexity 这类生成式引擎聊到相关话题时提到你。

这套架构对三者是同时生效的——因为 GPTBot、ClaudeBot、PerplexityBot 都在爬虫名单里,它们之前同样拿不到 JS 渲染的内容。要让 AI 愿意引用,内容形态比技术更重要:开头直接给结论(BLUF 原则)、多放对比表格、"一句话记住"这类可摘录的压缩表达,这种引用对于我们这种的个人网站来说是很可观的。

上线清单与后续

  • Thymeleaf 模板必须起后端实测——编译通过只说明 Java 没问题,模板表达式错误只有渲染时才炸。用 java -jar --server.port=8081 起一个,curl 爬虫 UA 确认拿到完整 HTML 再挂 Nginx。
  • sitemap 同步成新路径后再提交 GSC 和 Bing 站长工具(URL 检查 → 请求编入索引),百度站长平台同步提交——百度 JS 渲染最弱,是这套方案收益最大的搜索引擎。
SPA 不是 SEO 的死刑判决,但空壳 HTML 是——让爬虫和人类看到同一份内容,只是用各自能读懂的方式。

整个过程最大的收获是想明白了一个判断标准:方案好不好,不看它多正统,看它和现有架构的摩擦力有多大。Nuxt 是标准答案,但对我这个"Spring Boot + Vue3 SPA + 每天发一篇文章"的站点,后端动态渲染是摩擦力最小的那条路——前端一行没动,爬虫拿到的东西翻了一倍不止。

常见问题

  • SPA 博客搜索引擎完全不收录吗?Google 能执行 JS,理论上有机会,但渲染队列排期长、预算有限,收录慢且不稳定;Bing 支持不完整;百度基本只读初始 HTML。靠等不如主动给 HTML。
  • 按 UA 返回不同内容会被 Google 判作弊吗?不会,这是 Google 官方文档认可的 Dynamic Rendering。红线是内容等价:爬虫和用户拿到同一份数据渲染的结果,不能给爬虫塞用户看不到的内容。
  • 为什么不用 Nuxt 重写?整个前端要迁移、还要长期维护 Node 渲染服务。我的场景里 Spring Boot 吐 HTML 的成本几乎为零(正文本来就是 HTML 字符串),摩擦力最小。
  • 预渲染(prerender)为什么不行?构建时生成静态页,只适合页面固定的站。动态发文的内容站每发一篇就要重新构建,不现实。
  • AI 爬虫(GPTBot 等)需要单独处理吗?把它们加进 Nginx 的爬虫 UA 名单即可,和搜索引擎共用同一套渲染逻辑。想让 AI 引用内容,robots.txt 显式 Allow + 内容本身结构化(结论前置、表格、FAQ)比技术手段更重要。

← 返回文章列表