Vite-SSG 实践:Vue项目预渲染落地完整方案

本文记录 Vue 项目基于 vite-ssg 实现静态预渲染(SSG)的落地经验,适合官网类站点,后续同类官网可直接参考这套实现

一句话先看懂:本文方案基于 vite-ssg 做Vite预渲染,不是Nuxt那种运行时SSR。开发时仍是普通SPA单页应用,仅在打包构建阶段,把指定路由提前生成独立HTML,方便搜索引擎抓取页面标题、描述、正文和meta信息。

1. 构建产物是什么样?

打包后的dist目录是嵌套式HTML结构,Nginx配合目录默认首页规则,浏览器地址栏不会带.html后缀。

路由**** 构建产物**** 访问地址****
/ dist/index.html 线上站点根路径
/about dist/about/index.html /about
/blog/post-detail dist/blog/post-detail/index.html /blog/post-detail

💡原理:打包的时候就提前生成好about/index.html这份完整HTML。 把dist丢服务器作为静态目录,用户访问/about时,Nginx查找逻辑:

  1. 先找名为about的文件 → 不存在
  2. 再找about文件夹 → 存在
  3. 读取文件夹内默认首页index.html返回给浏览器

Nginx核心配置:

location / {

try_files uriuri uriuri/ /index.html;

}

  • $uri:匹配对应静态文件(js、图片等资源)
  • $uri/:匹配目录,返回目录下index.html(SSG页面就是走这条)
  • /index.html兜底:上面都匹配不到,回退SPA首页,用于没有预渲染的路由

用户地址栏看不到index.html,这只是服务器内部文件命名。

2. 需要配置哪些内容?

A. 依赖 & package.json脚本

用到两个核心包

  • vite-ssg@^28.3.0:静态预渲染构建
  • @unhead/vue:Vue内维护title、meta、canonical等head标签,预渲染时直接注入HTML

canonical 读音 /kəˈnɒnɪkəl/,SEO里叫规范URL,用来指定页面权威地址。

"scripts": {

"dev": "vite",

"build": "vite-ssg build",

"build:spa": "vite build",

"build:test": "vite-ssg build --mode development",

"build:prod": "vite-ssg build --mode production"

},

  • 本地开发npm run dev:还是普通SPA,不走SSG
  • 正式发布必须用vite-ssg build,不能直接vite build
  • build:spa作为兜底方案:SSG打包出问题时,可以临时切回普通SPA发布
  • npm run build等价build:prod,默认生产环境;build:test对应测试环境

B. 入口 main.js 修改

普通SPA写法:直接createApp然后mount挂载

const app = createApp(App);

app.use(getRouter());

app.mount('#app');

SSG改造:导出app工厂函数,让vite-ssg在Node环境逐个渲染页面

export const createApp = ViteSSG(

App,

{

routes,

scrollBehavior() {

return { top: 0 };

}

}

);

👉 scrollBehavior是VueRouter配置,不是SSG必需配置 作用:站内导航客户端跳转时页面自动滚回顶部;不加的话,切换路由会保留上次滚动位置,体验不好。 页面直接刷新/预渲染页面打开,浏览器本身就从顶部加载,这个配置只作用于站内点击导航。

C. 路由改造

src/router/index.js,必须单独导出routes数组,不要只导出router实例 vite-ssg读取的是路由表,不是运行时router。

  • 需要SEO收录的路由,添加meta.seoKey,供usePageSeo使用
  • 动态路由/blog/:id可以保留,但预渲染列表里,要展开成一个个真实path

D. vite.config.js 中的ssgOptions(核心配置)

ssgOptions: {

script: 'async',

formatting: 'minify',

dirStyle: 'nested',

includedRoutes() {

return prerenderRoutes; // 需要预渲染的全部路由

},

onFinished() {

writeSeoFiles(path.resolve(__dirname, 'dist'), siteUrl, mode === 'production');

}

},

配置项**** 说明**** 项目效果****
script: 'async' html内script模块增加async 优先展示预渲染页面内容,JS延迟水合,首屏更快
formatting: 'minify' 压缩HTML代码 文件体积更小,dist里html为单行代码
dirStyle: 'nested' 嵌套目录输出 /about生成about/index.html,适配Nginx静态托管;默认flat会输出about.html
includedRoutes 预渲染白名单 只有写在这里的路径,才生成HTML;动态blog路由需要展开所有id,否则会被丢弃
onFinished 全部页面渲染完成后的钩子 自动生成robots.txt、sitemap.xml,复制处理隐私协议页面

关键:includedRoutes + dirStyle 才决定会不会生成独立页面HTML;script、formatting只影响性能与文件格式。

vite-ssg参考资源:

其他可选参数简要说明:

  • entry:自定义入口文件路径
  • mock:预渲染模拟window/document,尽量少用,优先使用window判断守卫
  • concurrency:并发预渲染页面数量,页面量大可调整
  • beastiesOptions:内联关键CSS,false代表关闭

✅ 项目规则: 新增页面流程:添加路由 → 加入prerenderRoutes数组 → 需要收录就加到sitemapPaths 隐私页面特殊处理:路由存在,但不加入预渲染名单;onFinished钩子单独拷贝文件。 隐私页、注销账号页面即使预渲染,也不加入sitemap,robots里禁止爬虫抓取。

E. SEO三件套,页面被搜索引擎抓取的关键

  1. src/const/seo.js:维护每页title、description、keywords、robots规则
  2. src/composables/usePageSeo.js:封装@unhead/vue,把seo信息注入页面head
  3. App.vue 全局调用usePageSeo()

环境区分:

  • build:prod生产环境:普通页面index,follow;隐私、注销页面noindex
  • build:test测试环境:全站noindex,nofollow,防止测试站被搜索引擎收录

📖 noindex / nofollow 通俗解释: 这是给爬虫的指令,不影响用户正常访问页面****

  • noindex:不要把这个页面放进搜索结果
  • nofollow:不要抓取页面里的链接

常见组合:

  • index,follow:正常公开页面(首页、about、博客)
  • noindex,nofollow:隐私协议、注销页,禁止收录和爬取内链

区分:meta robots标签是爬虫抓到页面后生效;robots.txt是告诉爬虫尽量不要访问这个路径,两者配合使用。

F. 浏览器API兼容(踩坑重点!)

预渲染是跑在Node环境,不存在window、document,访问就会打包失败。 项目防护方案,新项目直接抄:

  1. src/utils/adapt.js:增加typeof window === 'undefined'判断,Node环境直接return
  2. src/utils/gsap.js:仅window存在时才注册ScrollTrigger
  3. 动画相关逻辑全部放到onMounted内部,不在setup顶层执行

简单一句话:Node预渲染阶段避开浏览器API;要使用window/document,先判断环境,或者放到onMounted里。

页面静态正文数据(博客内容),要打包时就拿到,不要在setup顶层发起接口请求。接口请求放到客户端执行。

3. OG社交分享卡片配置(Facebook/Zalo等平台)

用来控制分享链接时展示的卡片信息

标签**** 说明****
og:title 卡片标题,复用页面title
og:description 卡片摘要描述,复用页面description
og:url 页面完整绝对地址
og:type 普通页面website,博客详情article
og:image 分享大图,默认图og-default.png
og:image:type 图片类型,png
og:image:width/height 1200 × 630
og:site_name 站点名称
og:locale 语言区域vi_VN
og:image:alt 图片无障碍描述,可选

⚠️注意:Facebook会缓存分享卡片。修改图片/文案后,需要到调试工具重新抓取刷新: developers.facebook.com/tools/debug...

图片规范需求:

  • 尺寸 1200 × 630(比例1.91:1,Facebook/Zalo/Telegram通用)
  • 格式优先JPG,体积尽量<300KB,不超过1MB
  • 安全边距:四周预留80~100px,重要文字logo不要贴边,防止平台裁切
  • 文字:越南语,字号保证手机预览清晰
  • 内容:品牌Logo + 一句slogan;先做全站默认图,博客详情封面后续按需补充

4. 域名规范(重要)

正式域名:xxx.com 隐私协议正文里写www.xxx.com,需要运维配置:www.xxx.com 301重定向到裸域。

canonical规范URL只能保留唯一域名,避免重复收录问题。

总结

SSG静态站点生成,就是打包构建阶段提前把路由渲染成完整HTML。用户打开页面就能直接看到完整内容和SEO标签,不用等待浏览器加载Vue后再渲染。后续同类官网项目做SSG,可参考这套实现。

相关推荐
默_笙1 小时前
🍕 AI 的嘴巴装了水管(上):从"等它说完"到"边说边听"的流式输出指南
前端·javascript
lichenyang4531 小时前
让 VK 小程序调用 HarmonyOS 原生能力:壳子 SDK 的实现思路
前端
万敏1 小时前
Vue3 全栈实战:第一阶段复盘(第1-8周)
vue.js·node.js·全栈
梨想橙汁1 小时前
Vue3 组合式 API 深度解析:ref/reactive 响应式,计算属性与侦听器
前端·vue.js
暖焰核心1 小时前
继承全解——继承、默认成员函数、切片、隐藏与虚继承
java·前端·javascript
by组态2 小时前
Ricon组态系统API参考手册
前端·后端·物联网
前端逗比逗2 小时前
AI 前端落地实战:SSE 流式输出、断点续传、打字机渲染
前端·webassembly
晴天162 小时前
前端跨域方案解析:JSONP 的原理、实战与演进
前端·状态模式