Vue 单页面(SPA)和多页面(MPA)的区别
SPA 和 MPA 描述的主要是页面导航与文档生命周期;CSR、SSR、SSG 描述的是HTML 在什么时候生成。这几个概念经常一起出现,但不能直接画等号。
本文保留原文的入门对比,同时补充 Vue Router 4、Vue SSR、Nuxt 4、History API 和现代 SEO 的说明。
一、什么是 SPA
SPA(Single Page Application,单页面应用)通常指:浏览器第一次打开应用时获得一个应用文档,之后站内导航主要由客户端路由接管,在同一个 Document 内切换视图,而不是每次都进行完整的文档导航。
在 Vue 中,Vue Router 会根据当前 URL 选择组件并更新页面:
用户点击站内链接
↓
客户端路由拦截导航
↓
更新 URL 和当前路由状态
↓
切换 Vue 组件/视图
SPA 常见的体验是首屏加载后,页面之间切换不需要整页刷新。但“SPA 只有一个 HTML 文件”“SPA 只需要一次请求”都只是某些 CSR 部署的简化描述,不是 SPA 的严格定义:
- 首次访问仍然需要一次文档导航;
- 还可能加载 JavaScript、CSS、字体、图片、代码分块和接口数据;
- 路由切换可能加载新的路由 chunk 或请求 API;
- SPA 也可以使用 SSR/SSG 首次输出 HTML,再在客户端 hydration 后使用 SPA 式导航。
SPA 不等于 CSR
CSR(Client-Side Rendering,客户端渲染)是指主要由浏览器中的 JavaScript 生成页面内容。典型的 Vue SPA 经常采用 CSR,但两者不是同一个维度:
| 概念 | 关注点 |
|---|---|
| SPA | 导航是否在同一个文档内由客户端路由更新 |
| MPA | 不同 URL 是否通常对应不同 HTML 文档 |
| CSR | HTML 主要由浏览器 JavaScript 生成 |
| SSR | HTML 在请求时由服务端生成 |
| SSG | HTML 在构建时预先生成 |
一个 Nuxt 应用可以首屏 SSR,客户端 hydration 后采用 SPA 式导航;它仍可以拥有 SPA 的导航体验,但不属于“纯 CSR SPA”。
二、SPA 的基本原理
1. Hash 路由
最简单的客户端路由可以监听 URL 片段变化:
window.addEventListener('hashchange', () => {
console.log(window.location.hash)
})
例如:
https://example.com/#/about
https://example.com/#/products
# 后的片段不会作为 HTTP 请求路径发送给服务器,因此服务器通常只需要返回同一个入口文档。浏览器在片段变化时不会重新加载整个文档,并会触发 hashchange 事件。
这并不是 Vue 特有的能力。hashchange 是浏览器 Window 事件,Vue 可以监听,Vue Router 也提供了更完整的 hash history 实现。

2. History 路由
History 模式使用 History API 改变正常路径:
import {
createRouter,
createWebHistory,
} from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: () => import('./views/Home.vue') },
{ path: '/about', component: () => import('./views/About.vue') },
],
})
URL 看起来像普通页面:
https://example.com/about
客户端导航时,Router 使用 pushState、replaceState 和 popstate 管理历史记录。直接刷新 /about 时,浏览器会向服务器请求 /about,所以部署服务器必须将应用路由回退到 index.html,同时不要把真实静态资源和 API 请求无条件改写到入口文件。
Vue Router 4 的模式已经不是 Vue Router 3 的 mode: 'history' 配置,而是:
createWebHistory()
createWebHashHistory()
createMemoryHistory()
3. 原生 History API 的边界
pushState() 和 replaceState() 修改历史记录时不会自动触发 popstate;后退、前进等历史记录遍历才会触发 popstate。pushState() 也不会触发 hashchange。
如果只是学习原理,可以写一个简单 hash 状态组件:
<script setup lang="ts">
import {
onMounted,
onUnmounted,
ref,
} from 'vue'
const hash = ref('')
function syncHash() {
hash.value = window.location.hash
}
onMounted(() => {
syncHash()
window.addEventListener('hashchange', syncHash)
})
onUnmounted(() => {
window.removeEventListener('hashchange', syncHash)
})
</script>
<template>
<p>当前片段:{{ hash }}</p>
</template>
在 Nuxt 或 SSR 环境中,不要在模块顶层直接访问 window、document;应放在 onMounted 或客户端专用代码中。实际项目如果已经使用 Vue Router,通常应读取 useRoute(),而不是重复实现一套原生监听器。
原文“Vue 不支持 onhashchange”是错误的。正确说法是:Vue Router 负责封装路由状态,原生 hashchange 仍然属于浏览器 API;使用 Router 时一般不需要自己重复绑定。
三、SPA 的优点
1. 后续导航体验通常较好
首次加载并完成必要的 JavaScript 后,站内导航可以只更新需要变化的视图,而不必重新请求和解析完整 HTML 文档。用户的部分状态也可以继续保留。
但这不是“后续切换完全没有网络请求”:
- 路由组件可能是懒加载 chunk;
- 页面数据可能需要重新请求;
- 图片、字体或第三方资源可能延迟加载;
- 缓存、Service Worker 和 CDN 可能改变实际网络行为。
2. 交互状态更容易连续保留
例如筛选条件、已打开的弹窗和客户端 Store 可以在视图切换时保留。不过是否保留由应用逻辑决定,路由切换、KeepAlive、Store 和页面卸载都可能改变状态生命周期。
3. 前后端接口边界清晰
前端可以通过 API 获取数据,后端提供认证、数据和业务接口。这个边界适合管理后台、SaaS、在线编辑器等强交互应用,但也需要额外处理鉴权、首屏数据、错误、加载状态和 SEO。
四、SPA 的缺点与现代解决方案
1. 首屏可能较慢
CSR SPA 首次打开通常需要经历:
HTML 文档
↓
加载并执行 JS/CSS
↓
创建 Vue 应用
↓
请求首屏数据
↓
生成可交互内容
不能把它固定成“正好两次 HTTP 请求”。一次 HTML 导航会启动资源瀑布,具体请求数量取决于打包、预加载、缓存、API 和部署方式。
可采用:
- 路由级代码分割;
import()异步加载非首屏模块;- 预加载关键 chunk;
- 图片和字体优化;
- SSR/SSG/prerender;
- 合理的首屏数据获取策略;
- 避免把所有代码都打进首屏入口。
2. SEO 需要额外设计
“搜索引擎完全不认识 JavaScript”已经不是准确的现代表述。Google Search 会经历 crawl、render、index 阶段,并且能够执行 JavaScript,但渲染可能有延迟,也不能代表所有搜索引擎、社交爬虫和站内抓取器。
公开且重要的内容更稳妥的方案是:
- 使用 SSR、SSG 或 prerender,让正文出现在初始 HTML;
- 每个内容页面使用稳定、可访问的 URL;
- 输出唯一的
title、description、canonical 和结构化数据; - 使用正常的
<a href>/<NuxtLink>让爬虫发现页面; - 不要把正文完全隐藏在点击、权限或客户端状态之后;
- 处理真实 404,避免所有未知 URL 都返回应用首页造成 soft 404。
SSR/SSG 能改善首屏 HTML 和抓取可见性,但不是排名保证,也不是自动解决所有性能和 SEO 问题。SSR 还会带来服务端成本、hydration、一致性和浏览器 API 隔离等要求。
五、什么是 MPA
MPA(Multi-Page Application,多页面应用)通常指不同页面 URL 对应不同的 HTML 文档。用户从 /home 导航到 /products 时,浏览器通常执行一次新的文档导航,服务器或静态主机返回另一份页面文档。
MPA 不代表每次导航只有一个 HTTP 请求。HTML 返回后仍可能触发:
- CSS、JavaScript、字体和图片请求;
- 接口或服务端数据请求;
- 第三方脚本请求;
- 重定向、缓存验证和 CDN 请求。
MPA 也不代表页面完全没有客户端渲染。每个页面可以使用 Vue 做局部增强,也可以使用 CSR 组件、局部 hydration 或前端交互。
原文的第二张配图把 MPA 误写成了 “MAP”,且图片属于历史抓取内容,因此不直接嵌入本文;正文以 MPA(Multi-Page Application)为准。
MPA 的优点
- 每个页面通常都有独立的 HTML、URL、标题和状态码;
- SSR 或 SSG 内容天然容易被不执行 JavaScript 的爬虫读取;
- 单个页面的 JavaScript 体积可能较小;
- 页面之间相互隔离,适合内容站、营销页、传统电商和多团队独立维护;
- 可以使用成熟的服务端路由、缓存和权限控制。
“MPA 首屏只需要一个 HTTP 请求所以一定更快”并不准确。服务端处理时间、数据库查询、HTML 大小、资源数量和缓存命中都会影响性能。
MPA 的缺点
- 跨页面导航通常会替换整个文档;
- 页面级状态需要重新初始化或额外持久化;
- 过多的全量导航可能造成明显的页面闪烁;
- 多个页面可能重复加载公共资源;
- 如果每个页面都重复集成脚本和数据逻辑,维护成本会增加。
现代构建工具可以共享公共 chunk、使用预加载和缓存,因此“MPA 每个页面一定重复加载全部 JS/CSS”也是过度绝对化的说法。
六、SPA 与 MPA 对比
原文的第三张对比图包含“MPA 不共用资源”“每页都要重新加载全部资源”“MPA 无法实现转场动画”等过时的绝对表述,已不直接嵌入;现代缓存、公共 chunk 和渐进增强请以本文对比表为准。
| 对比项 | SPA | MPA |
|---|---|---|
| 文档导航 | 首次访问是真实文档导航,之后通常由客户端路由软导航 | 不同页面通常进行新的文档导航 |
| 页面更新 | 在同一文档内更新组件/视图 | 获取并替换新的 HTML 文档 |
| 首屏方式 | 可以 CSR,也可以 SSR/SSG 后 hydration | 可以 SSR、SSG,也可以包含 CSR 局部组件 |
| 请求数量 | 首次需要 HTML、资源和可能的 API;软导航仍可能请求 chunk/API | 每次文档导航会获取 HTML,并可能触发多个子资源请求 |
| 状态 | 客户端状态较容易跨视图保留 | 页面切换时通常重新初始化 |
| SEO | CSR 需要额外处理;SSR/SSG 更容易提供初始 HTML | 独立 HTML 页面通常更直接,但仍需正确 metadata/状态码 |
| 部署 | History 模式需要 fallback;Hash 模式部署简单但 SEO 较弱 | 服务端/静态主机按页面路径提供对应文档 |
| 典型场景 | 后台、SaaS、在线编辑器、复杂交互应用 | 博客、文档、营销页、内容站、传统电商 |
关键结论: SSG 生成多个 HTML 文件并不自动等于 MPA;如果 hydration 后使用客户端路由,仍然可以拥有 SPA 式导航。反过来,MPA 也可以在每个页面内使用 Vue 和客户端渲染。
七、SSR、SSG 与混合渲染
1. SSR
Vue SSR 会在服务端生成 HTML,浏览器收到 HTML 后再进行 hydration,重新绑定事件并变成可交互的 Vue 应用:
请求页面
↓
服务端执行组件并生成 HTML
↓
浏览器先显示 HTML
↓
下载 JS 并 hydration
↓
页面获得交互能力
SSR 可以改善首屏内容可见性和抓取稳定性,但要注意:
- 服务端不能直接使用
window、document等浏览器 API; - 服务端和客户端的初始渲染结果应保持一致;
- hydration mismatch 会导致警告或额外修复;
- 每次请求都可能产生服务端计算和数据访问成本;
- 缓存策略、流式渲染和错误页面需要单独设计。
参考:Vue SSR。
2. SSG / prerender
SSG 在构建阶段生成 HTML,部署时由 CDN 或静态主机直接提供。适合文章、文档、营销页等变化频率较低的内容。
SSG 也不决定导航形式:
- 生成多个页面并使用普通链接,可呈现 MPA 导航;
- 生成多个页面并在 hydration 后使用客户端 Router,也可呈现 SPA 式导航;
- 动态路由需要确保在构建阶段被发现或显式加入 prerender 配置。
3. Nuxt 4 的情况
Nuxt 4 默认采用 universal rendering:首个请求通常由服务端渲染,客户端 hydration 后,后续页面导航使用客户端路由。也可以根据路由选择:
ssr: false:纯客户端渲染,适合部分后台或 SaaS 场景;- prerender:构建时生成静态页面;
- SSR:请求时生成 HTML;
- route rules:对不同路由混合使用 prerender、SSR、SWR/ISR 和客户端渲染。
因此,现代 Vue/Nuxt 项目往往不是简单二选一,而是按页面目标混合选择。
八、History、Hash 与部署
History 模式
Vue Router 4:
import {
createRouter,
createWebHistory,
} from 'vue-router'
export const router = createRouter({
history: createWebHistory(),
routes,
})
静态主机通常需要类似以下回退逻辑:
如果请求的是已有文件或真实 API:正常返回
否则把前端路由请求回退到 index.html
以 Nginx 为例,常见思路是:
location / {
try_files $uri $uri/ /index.html;
}
实际配置还要排除 API、静态资源,并处理真实的 404。不能把所有未知路径都无条件返回 200,否则可能形成 soft 404。
Hash 模式
import { createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes,
})
Hash 后的内容不发送到服务器,所以不需要配置 History fallback,适合无法控制服务器重写规则的场景。但 /#/products 不如正常路径适合公开内容的 SEO,公开站点通常优先考虑 History + SSR/SSG/prerender。
九、如何选择
适合 SPA 或 SPA 式导航的场景
- 管理后台;
- SaaS 和工作台;
- 在线编辑器、绘图和协作工具;
- 需要大量局部状态和复杂交互的应用;
- 登录后使用、无需公开搜索的业务系统。
适合 MPA、SSR 或 SSG 的场景
- 博客、技术文档和新闻站;
- 营销落地页;
- 商品详情和公开内容页;
- 需要稳定初始 HTML、状态码和社交分享预览的页面;
- 多个独立团队维护的页面集合。
混合方案
一个真实项目可以同时包含:
- 首页和文章页使用 SSG/SSR;
- 登录后的控制台使用客户端渲染;
- 商品列表使用 SSR,筛选结果通过客户端请求更新;
- 某些路由使用 History,特殊嵌入页面使用独立文档。
不要先争论“SPA 还是 MPA 一定更好”,应先看页面的首屏、SEO、交互、数据新鲜度、部署能力和团队维护成本。
十、SEO 与上线检查清单
- 每个需要索引的页面都有稳定 URL;
- 页面有唯一
title、description 和 canonical; - 重要正文在初始 HTML 或稳定的 SSR/SSG 结果中可见;
- 使用可发现的
<a href>/<NuxtLink>,不要只依赖点击事件; - 不要阻断必要的 JavaScript、CSS 和数据资源;
- 直接打开和刷新深层 URL 不应 404;
- 真正不存在的页面返回真实 404,不要全部重定向到首页;
- 客户端导航后同步更新标题和结构化信息;
- 检查 hydration mismatch、JS 错误和 API 超时;
- 使用浏览器禁用 JS、冷缓存、移动网络和真实深层 URL 测试;
- 使用搜索引擎提供的 URL Inspection 或渲染工具查看最终 DOM。
十一、总结
- SPA 关注同一文档内的客户端导航,MPA 关注不同 URL 对应不同文档;
- SPA 不等于 CSR,MPA 也不等于完全没有客户端渲染;
- SPA 首屏不一定只有两次请求,MPA 导航也不一定只有一次 HTTP 请求;
- 搜索引擎可以执行部分 JavaScript,但公开内容仍建议使用 SSR、SSG 或 prerender 提供稳定 HTML;
- Vue Router 4 使用
createWebHistory、createWebHashHistory等 API,History 模式需要服务器 fallback; hashchange是浏览器原生事件,Vue 和 Vue Router 都可以使用它;- Vue/Nuxt 4 支持按路由混合 SSR、SSG、CSR 和客户端导航,不必把项目限制在单一模式中。
官方参考
- Vue Routing
- Vue SSR
- Vue Router 4 History 模式
- Vue Router 4 迁移指南
- Vue Router 4
createWebHashHistory - Vue Router 4 路由懒加载
- MDN History API
- MDN
hashchange - MDN
popstate - Nuxt 4 Rendering Modes
- Nuxt 4 Prerendering
- Nuxt 4 Routing
- Google JavaScript SEO Basics
原文出处
作者:佑子呀
来源:稀土掘金
本文保留原文 SPA/MPA 对比和路由原理,并补充 CSR、SSR、SSG、Vue Router 4、Nuxt 4 与 SEO 的现代说明。