注意问题
插件是 vite-ssg 而不是老版本的vite-plugin-ssg
usehead 要用 @unhead/vue 不要用老版本的 @vueuse/head
别忘了配置服务器相关内容(如caddy/nginx)
VUE 页面与SEO问题
Vue 默认构建的是单页应用 (SPA),依赖 JS 动态渲染来实现无刷新跳转,体验流畅。
但这会导致 SEO 问题,因为爬虫难以抓取动态生成的 Title 和 Meta。
因此,我们需要将页面预渲染为独立的静态 HTML 文件,确保搜索引擎能直接读取内容。
解决方法: vite-ssg多页面生成SSG and 水合(hydration)
应该的工作流程是:
加载(静态文件) 用户访问 /abc,服务器隐式返回 abc.html 的文件内容。浏览器拿到后直接渲染 HTML,用户立刻看到完整的页面(标题、文字、布局),此时页面是静态的。
SEO 可以检索
JS (水合)Vue 启动后,根据 URL /abc 匹配组件,生成虚拟 DOM。它与当前页面真实的 DOM 对比,发现结构一致,便不再重新渲染,而是直接将事件监听器(如点击事件)挂载到现有 DOM 上。页面从“静态”变为“动态”。
后续跳转(SPA 模式) 水合完成后,Vue Router 全权接管页面。当用户点击内部链接时,Router 拦截浏览器默认跳转,使用 History API 修改地址栏 URL,并在客户端直接替换组件内容(销毁旧组件,渲染新组件)。后续操作不再请求 HTML 文件。
其实这个 静态 HTML 文件 只是用来优化SEO和作为入口文件的,实际上访问是还是SPA。
SSG 配置
安装 vite-ssg vue-router @unhead/vue
npm i -D vite-ssg vue-router @unhead/vue
main 文件
// src/main.ts
import { ViteSSG } from 'vite-ssg'
import App from './App.vue'
// `export const createApp` is required instead of the original `createApp(App).mount('#app')`
export const createApp = ViteSSG(
// the root component
App,
// vue-router options
{ routes },
// function to have custom setups
({ app, router, routes, isClient, initialState }) => {
// install plugins etc.
},
)
页面配置 head
<template>
</template>
<script setup>
import { useHead } from '@unhead/vue'
useHead({
title: 'Website Title',
meta: [
{
name: 'description',
content: 'Website description',
},
],
})
</script>
路由文件
export default [
{
path: '/',
component: BaseTemplate,
children: [
{ path: '/', name: 'home', component: HomePage },
...Routes,
],
},
]
package 配置
添加红色行
{
"name": "xxx",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build ",
"preview": "vite preview",
"buildssg": "vite-ssg build "
},
"dependencies": {
"@vueuse/head": "^2.0.0",
"axios": "^1.13.2",
"compressorjs": "^1.2.1",
"echarts": "^6.0.0",
"file-saver": "^2.0.5",
"jszip": "^3.10.1",
"sitemap": "^9.0.0",
"vue": "^3.5.24",
"vue-echarts": "^8.0.1"
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.17",
"@unhead/vue": "^2.0.19",
"@vitejs/plugin-vue": "^6.0.1",
"daisyui": "^5.5.5",
"tailwindcss": "^4.1.17",
"vite": "^7.2.4",
"vite-plugin-sitemap": "^0.8.2",
"vite-ssg": "^28.2.2",
"vue-router": "^4.6.3"
}
Caddy 配置
xxx.com {
# 1. 设置网站根目录
root * /var/www/site
encode gzip zstd
# 3. 核心逻辑:文件查找顺序
# 当用户访问 /abc 时,Caddy 会按顺序尝试查找文件:
# A. {path}: 是否存在名为 abc 的文件?(通常用于图片、JS资源)
# B. {path}.html: 是否存在 abc.html?(隐式发送内容,URL 不变)
# C. {path}/index.html: 是否存在 abc/index.html?(如果改用了目录结构方案)
# D. /index.html: 都没找到?发送首页 (兜底,交给 Vue Router 处理 404 或动态路由)
try_files {path} {path}.html {path}/index.html /index.html
# 4. 开启静态文件服务
file_server
}