Vue进阶系列第3篇:什么是SPA(单页面应用)? 如何实现一个简单的SPA?

Last updated on December 28, 2025 pm

大家好,我是jvxiao。

这是Vue从入门到精通系列文章的第3篇,今天我们来谈谈和Vue紧密相关的一个东西–SPA。此SPA非各位日常生活中的养生SPA, 而是单页面应用(Single Page Application)。

在现代前端开发中,单页面应用(SPA)已成为主流架构之一,Vue、React、Angular 等框架均以 SPA 为核心设计理念。它凭借流畅的交互体验、高效的资源利用,广泛应用于后台管理系统、移动端应用等场景。

本文将从基础概念出发,对比 SPA 与传统多页面应用(MPA)的核心差异,并通过极简代码实现 hash 模式 SPA,帮助大家快速掌握其工作原理。

一、什么是 SPA(单页面应用)

SPA(Single Page Application)即单页面应用,是一种仅加载一个核心 HTML 文件的 Web 应用架构。其核心特征是:页面内容的更新不依赖浏览器的整页刷新,而是通过 JavaScript 动态操作 DOM 节点、异步请求数据,实现视图的局部更新。

可以通俗地理解为:SPA 就像一个 “固定外壳” 的房子,首次加载时会完整加载房屋的基础结构(核心 HTML、CSS、JS),后续切换 “房间”(页面视图)时,无需更换房子本身,只需通过 JS 动态替换内部的 “家具”(DOM 内容)。这种设计让应用交互更接近原生 App,避免了传统页面跳转时的加载等待感。

SPA 的核心技术支撑包括:

  • 前端路由:管理 URL 与视图的映射关系,实现无刷新跳转;
  • 动态 DOM 操作:通过 JS 修改页面内容,而非重新加载 HTML;
  • 异步数据请求:通过 AJAX/ Fetch 获取后端数据,更新视图内容。

二、SPA 与 MPA(多页面应用)的核心差异

传统多页面应用(MPA,Multi Page Application)与 SPA 在架构设计、交互逻辑上存在本质区别,以下从 6 个核心维度对比分析:

1. 页面结构​
SPA:仅包含 1 个核心 HTML 文件,所有视图均基于该文件通过 JS 动态生成;​
MPA:包含多个独立的 HTML 文件,每个页面对应一个单独的 HTML 资源(如首页 index.html、关于页 about.html)。​

2. 跳转机制​
SPA:通过前端路由实现跳转(如 hash/history 模式),URL 变化时不会触发浏览器整页刷新,仅更新局部 DOM;​
MPA:通过浏览器原生跳转(a 标签、表单提交、location.href)实现页面切换,每次跳转都会重新加载整个 HTML、CSS、JS 资源,浏览器会显示加载状态。​

  1. 资源加载
    SPA:首次加载时会加载所有核心资源(框架、公共 JS/CSS、路由配置),后续仅需按需加载接口数据,资源复用率高;​
    MPA:每次页面跳转都需重新加载对应页面的全部资源(HTML 结构、页面专属 CSS/JS),资源重复加载较多。​

  2. 交互体验
    SPA:首次加载可能因资源较多稍慢,但后续交互无刷新延迟,流畅度接近原生应用;​
    MPA:每次跳转都存在资源加载等待,交互体验相对卡顿,尤其在网络环境较差时表现明显。​

  3. SEO 友好性
    SPA:天生存在 SEO 劣势 —— 页面内容由 JS 动态生成,传统搜索引擎爬虫难以抓取动态渲染的内容,需通过 SSR(服务端渲染)等方案优化;​
    MPA:页面内容静态嵌入 HTML,搜索引擎爬虫可直接抓取,SEO 天然友好,适合资讯、官网等需要曝光的场景。

  4. 适用场景
    SPA:更适合交互频繁、对体验要求高的应用,如后台管理系统、移动端 App、社交应用等;​
    MPA:更适合内容分散、无需复杂交互的场景,如资讯网站、企业官网、电商商品列表页等。

三、hash 模式 SPA 的实现原理与极简案例

在 SPA 的前端路由方案中,hash 模式是最简易、兼容性最强的实现方式。其核心依赖 URL 中 “#” 符号的特性:

hash 模式核心原理

  • URL 中 “#” 后面的部分(hash 值)属于客户端标识,其变化不会触发浏览器整页刷新,也不会向服务器发送请求;
  • 当 hash 值发生变化时,浏览器会触发 hashchange 事件,前端可监听该事件,根据不同的 hash 值动态更新视图。

基于这两个特性,可实现 “URL 变化→事件触发→视图更新” 的闭环,完成无刷新跳转。

极简 hash 模式 SPA 实现代码

以下是完整可运行的原生 JS 代码,保存为 spa-demo.html 直接在浏览器打开即可体验:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Hash模式极简SPA</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: "Microsoft YaHei", sans-serif; }
nav { background-color: #2c3e50; padding: 1.2rem 2rem; }
nav a { color: #ecf0f1; text-decoration: none; margin-right: 1.5rem; font-size: 1.1rem; }
nav a.active { color: #3498db; border-bottom: 2px solid #3498db; padding-bottom: 0.3rem; }
#view-container { padding: 2.5rem; font-size: 1.1rem; line-height: 1.8; }
h2 { color: #2c3e50; margin-bottom: 1rem; }
.404 { color: #e74c3c; }
</style>
</head>
<body>
<!-- 导航栏:触发hash变化的入口 -->
<nav>
<a href="#/home">首页</a>
<a href="#/about">关于我们</a>
<a href="#/contact">联系我们</a>
<a href="#/other">不存在的页面</a>
</nav>

<!-- 视图容器:动态更新的内容区域 -->
<div id="view-container"></div>

<script>
// 1. 定义路由规则:hash路径与页面内容的映射关系
const routeConfig = [
{
path: '#/home',
content: '<h2>首页</h2><p>欢迎使用hash模式SPA示例!这是应用的核心页面,所有内容均通过JS动态渲染。</p>'
},
{
path: '#/about',
content: '<h2>关于我们</h2><p>本示例基于原生JS实现,无需依赖任何前端框架,核心利用URL的hash特性实现无刷新跳转。</p><p>技术原理:hash变化触发hashchange事件,通过事件监听动态更新DOM内容。</p>'
},
{
path: '#/contact',
content: '<h2>联系我们</h2><p>邮箱:demo@spa-example.com</p><p>GitHub:https://github.com/spa-demo/hash-mode</p>'
}
];

// 2. 核心渲染函数:根据当前hash更新页面内容
function renderView() {
// 获取当前URL的hash值,若为空则默认跳转到首页
const currentHash = window.location.hash || '#/home';

// 匹配对应的路由规则,未匹配到则显示404
const matchedRoute = routeConfig.find(route => route.path === currentHash);
const viewContent = matchedRoute
? matchedRoute.content
: '<h2 class="404">404 页面不存在</h2><p>请检查URL是否正确,或返回首页重试。</p>';

// 动态更新视图容器内容
const viewContainer = document.getElementById('view-container');
viewContainer.innerHTML = viewContent;

// 高亮当前导航项(提升用户体验)
document.querySelectorAll('nav a').forEach(link => {
if (link.getAttribute('href') === currentHash) {
link.classList.add('active');
} else {
link.classList.remove('active');
}
});
}

// 3. 监听hash变化事件:当URL的hash改变时,重新渲染视图
window.addEventListener('hashchange', renderView);

// 4. 页面首次加载时初始化渲染(确保打开页面即显示对应内容)
window.addEventListener('load', renderView);
</script>
</body>
</html>

3. 代码核心逻辑解析

(1)路由规则定义(routeConfig)

路由表是 SPA 的 “导航地图”,定义了不同 hash 路径与页面内容的映射关系。实际项目中,“content” 会替换为 Vue/React 组件,此处用原生 HTML 字符串简化演示。

(2)渲染函数(renderView)

SPA 核心逻辑,负责根据当前 hash 更新页面内容:

  • 获取 hash 值:通过 window.location.hash 获取 URL 中 “#” 后内容,默认值为 #/home
  • 匹配路由:用 find 方法查找当前 hash 对应的路由,未匹配则返回 404;
  • 动态更新 DOM:将匹配到的内容插入视图容器 view-container,实现局部更新;
  • 导航高亮:通过操作 DOM 类名,高亮当前选中导航项,提升交互体验。

(3)事件监听

  • hashchange 事件:用户点击导航或手动修改 hash 时触发,调用 renderView 重新渲染;
  • load 事件:页面首次加载时触发,初始化显示首页内容。

SPA 的优势与局限

1. 核心优势

  • 交互体验流畅:无整页刷新,切换视图无延迟,接近原生 App;
  • 资源利用率高:首次加载核心资源后,后续仅加载数据,减少网络请求;
  • 前后端分离:前端独立控制渲染,后端仅提供接口,职责清晰;
  • 适配性强:易于适配移动端、桌面端等多终端。

2. 主要局限

  • 首次加载速度慢:需一次性加载核心 JS/CSS,资源体积大时会有延迟;
  • SEO 优化困难:传统爬虫难抓取动态内容,需 SSR/预渲染等方案优化;
  • 兼容问题:早期浏览器对 hashchange 支持有限(现代浏览器已兼容),history 模式需后端配置。

五、写在最后

如果你认真阅读了上述内容并自己跑了一遍代码,想必你也基本掌握了SPA的工作原理了。

在本文中实现的 hash 模式 SPA 存在 URL 含 # 的问题,实际项目中更常用 history 模式,核心依赖 HTML5 的 history.pushState() 和 history.replaceState() 方法,可实现无 # 的简洁 URL。

大家可以尝试对上述代码进行改造实现,也可以此基础上扩展:引入组件化、添加异步请求、实现路由守卫等,逐步接近真实项目架构。

上周有点懒,更新的频率有点低,这周争取3篇文章,最少两篇,感谢大家的支持哈!

【往期精彩】


Vue进阶系列第3篇:什么是SPA(单页面应用)? 如何实现一个简单的SPA?
https://www.jvxiao.cn/posts/what-is-spa/
Author
jvxiao
Posted on
December 8, 2025
Licensed under