2025-12-21 【架构】Vue3单页应用 SEO 改造最佳实践

Vue3 + TS + Vite 单页应用 SEO 改造最佳实践

在现代前端开发中,Vue3 + TypeScript + Vite 已经成为构建单页应用 (SPA) 的主流技术栈。然而,SPA 原生存在 SEO(搜索引擎优化)短板,因为其内容主要通过 JavaScript 动态生成,爬虫(尤其是国内的百度爬虫)可能无法正确抓取和索引页面内容。

本文将深入探讨针对 Vue3 SPA 项目的 SEO 改造最佳实践,重点介绍低成本、高收益的预渲染 (Prerendering) 方案。

🔍 SPA 的 SEO 痛点

  1. 内容抓取困难: 爬虫抓取到的 HTML 通常只有一个空的 <div id="app"></div>,内容需要 JS 执行后才渲染。
  2. 首屏加载慢: 需要下载并解析 JS 后才能显示内容,影响用户体验和搜索引擎评分 (Core Web Vitals)。
  3. Meta 信息缺失: 每个页面的 Title、Description 通常是固定的,无法根据路由动态变化。

🛠️ 解决方案对比

方案 描述 优点 缺点 适用场景
SSR (服务端渲染) 使用 Nuxt.js 或 Vite SSR,在服务端渲染 HTML。 SEO 效果最好,首屏极快。 开发/部署成本高,需要 Node.js 服务器支持。 大型内容型网站,对 SEO 要求极高。
SSG (静态站点生成) 构建时生成每个路由的静态 HTML。 纯静态文件,部署简单 (Nginx/CDN),SEO 好。 构建时间随页面数量增加,不适合频繁变动的数据。 博客、文档、营销页。
预渲染 (Prerendering) 构建后通过无头浏览器 (Puppeteer) 跑一遍页面,保存为 HTML。 侵入性小,无需改造代码架构,部署简单。 仅适用于静态路由,动态路由配置复杂。 中小型 SPA 项目 SEO 改造首选。

对于大多数已经开发完成的 SPA 项目,预渲染是性价比最高的改造方案。

🚀 实战方案一:基于 vite-plugin-prerender 的预渲染改造

⚠️ 注意:该插件依赖 Puppeteer (无头 Chrome),安装包体积较大且可能受网络环境影响导致安装失败。构建过程需要启动浏览器,速度相对较慢。适合对构建环境有完全控制权,且代码中包含大量依赖浏览器 API (如 window, document) 的场景。

我们将使用 vite-plugin-prerender 插件来实现预渲染。该插件基于 Puppeteer,在构建阶段启动一个无头浏览器,访问指定路由并将渲染结果保存为静态 HTML 文件。

1. 安装依赖

1
2
3
4
5
6
npm install vite-plugin-prerender puppeteer -D
# 或者
pnpm add vite-plugin-prerender puppeteer -D

"vite-plugin-prerender": "^1.0.8",
"puppeteer": "^19.7.0",

2. 配置 Vite (vite.config.ts)

vite.config.ts 中引入并配置插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
import vitePrerender from "vite-plugin-prerender";

export default defineConfig({
plugins: [
vue(),
vitePrerender({
staticDir: path.join(__dirname, "dist"),
routes: ["/about"],
renderer: new vitePrerender.PuppeteerRenderer({
maxConcurrentRoutes: 1,
renderAfterTime: 5000,
}),
postProcess(renderedRoute) {
renderedRoute.html = renderedRoute.html
.replace(/http:\/\/localhost:\d+/g, "")
.replace(/class="loader"/g, 'class="loader" style="display:none"');
return renderedRoute;
},
}),
],
});

3. 改造入口文件 (main.ts)

为了确保页面完全渲染后再进行抓取,我们需要在 Vue 挂载完成后触发一个事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";

const app = createApp(App);

app.use(router);

router.isReady().then(() => {
app.mount("#app");
// 触发预渲染事件,通知 Puppeteer 页面已准备好
document.dispatchEvent(new Event("render-event"));
});

4. 动态 Meta 标签管理

SEO 的核心之一是每个页面要有独立的 Title 和 Description。我们可以使用 @vueuse/head (或 unhead) 来管理。

安装:

1
npm install @vueuse/head

使用 (main.ts):

1
2
3
import { createHead } from "@vueuse/head";
const head = createHead();
app.use(head);

// 在页面组件中 (Home.vue):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<script setup lang="ts">
import { useHead } from "@vueuse/head";

useHead({
title: "首页 - 我的 Vue3 应用",
meta: [
{
name: "description",
content: "这是 Vue3 + Vite + TS 应用的首页,包含最新的技术分享。",
},
{ name: "keywords", content: "Vue3, Vite, TypeScript, SEO" },
// Open Graph 社交分享优化
{ property: "og:title", content: "首页 - 我的 Vue3 应用" },
{
property: "og:description",
content: "这是 Vue3 + Vite + TS 应用的首页,包含最新的技术分享。",
},
{ property: "og:image", content: "https://your-domain.com/share.png" },
],
link: [
// 规范化 URL,防止重复内容
{ rel: "canonical", href: "https://your-domain.com/" },
],
});
</script>

### 5. 构建与验证 运行构建命令: ```bash npm run build

构建完成后,查看 dist 目录,你会发现除了 index.html,还生成了 about/index.html, contact/index.html 等文件夹。

打开这些 HTML 文件,你会看到 <div id="app"> 内部已经有了完整的 DOM 结构和内容,而不仅仅是空的占位符。这就是预渲染的成果!

🚀 实战方案二:基于 vite-ssg 的 SSG 改造

✅ 推荐:相比于方案一,vite-ssg 基于 Node.js 环境直接渲染,无需启动无头浏览器,构建速度更快,依赖更轻量,是 Vue3 生态中更现代的静态化方案。

如果你希望获得更彻底的静态化体验,或者你的应用主要由静态内容组成(如博客、文档),vite-ssg 是一个非常优秀的 SSG 方案。它在构建时将 Vue 应用渲染为静态 HTML,同时保留了客户端的水合(Hydration)能力。

1. 安装依赖

1
2
3
4
5
npm install vite-ssg -D
# 路由依赖是必须的
npm install vue-router
# 推荐配合 @vueuse/head 管理 SEO 标签
npm install @vueuse/head

2. 改造入口文件 (main.ts)

vite-ssg 需要导出一个 createApp 函数,而不是直接挂载应用。注意:这与方案一的入口文件改造互斥,请根据选择的方案进行调整。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// main.ts
import { ViteSSG } from "vite-ssg";
import App from "./App.vue";
import routes from "./router/routes"; // 需将路由配置抽离为独立文件
import "./style.css";

// 导出 ViteSSG 函数
export const createApp = ViteSSG(
// 根组件
App,
// 路由配置
{ routes },
// 钩子函数
({ app, router, routes, isClient, initialState }) => {
// 在这里安装插件,例如 Pinia, i18n 等
// app.use(createPinia())
}
);

3. 配置 package.json

修改构建脚本以使用 vite-ssg 进行构建。

1
2
3
4
5
6
7
{
"scripts": {
"dev": "vite",
"build": "vite-ssg build",
"preview": "vite preview"
}
}

4. 局部页面静态化与配置

vite-ssg 默认会抓取所有路由。如果你只想针对特定页面(如首页 / 和关于页 /about)进行 SEO 优化,或者需要处理动态路由,可以在 vite.config.ts 中配置 ssgOptions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

export default defineConfig({
plugins: [vue()],
// SSG 专用配置
ssgOptions: {
script: "async",
formatting: "minify",
// 精确控制要生成的静态路由
includedRoutes(paths, routes) {
// 总是包含首页和 about 页,忽略其他
return ["/", "/about"];
// 或者基于爬取结果过滤
// return paths.filter(i => !i.includes('admin'))
},
// 处理动态路由的回调
onFinished() {
console.log("SSG 构建完成");
},
},
});

🌟 进阶优化

1. 生成 Sitemap

使用 vite-plugin-sitemap 自动生成站点地图,方便搜索引擎收录。

1
npm install vite-plugin-sitemap -D
1
2
3
4
5
6
7
8
9
10
11
12
// vite.config.ts
import Sitemap from "vite-plugin-sitemap";

export default defineConfig({
plugins: [
// ...
Sitemap({
hostname: "https://your-domain.com",
dynamicRoutes: ["/", "/about", "/contact"],
}),
],
});

2. Nginx 配置

预渲染生成的目录结构通常是 路由/index.html。Nginx 需要配置 try_files 以正确服务这些文件。同时建议开启 Gzip/Brotli 压缩以提升加载速度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {
listen 80;
server_name your-domain.com;
root /usr/share/nginx/html;
index index.html;

# 开启 Gzip 压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

location / {
# 尝试查找 $uri/index.html (预渲染文件) -> $uri -> index.html (SPA 回退)
try_files $uri $uri/index.html /index.html;
}
}

📝 总结

对于 Vue3 + Vite 的单页应用,预渲染 (Prerendering)SSG (静态站点生成) 是实现 SEO 最快、成本最低的路径。

  • 方案一 (vite-plugin-prerender): 适合老旧项目改造,或者强依赖浏览器环境的场景。但构建较慢,依赖 Puppeteer。
  • 方案二 (vite-ssg): 推荐首选。适合新项目或博客/文档类应用。构建快,依赖少,体验更接近原生 SSR。
  • 适用范围: 官网、营销页、文档站等静态内容较多的场景。
  • 局限性: 对于海量动态数据(如电商详情页),建议考虑 Nuxt.js SSR 方案。

通过本文的实践,你可以轻松让你的 Vue3 SPA 项目被搜索引擎友好收录,获取更多自然流量。


2025-12-21 【架构】Vue3单页应用 SEO 改造最佳实践
https://zhangyingxuan.github.io/SEO/2025-12-21 【SEO优化】Vue3单页应用 SEO 改造最佳实践/
作者
blowsysun
许可协议