Vue 3 中使用 Vue Router 实现 SPA(单页应用)

SPA(单页应用)是一种现代Web开发模式,通过动态重写当前页面实现交互,无需整页刷新。


其核心特点包括客户端路由、前后端分离和组件化开发。


相比传统多页应用,SPA具有更流畅的用户体验和更好的性能优化潜力,但也面临SEO和首屏加载的挑战。


主流框架如React、Vue和Angular都支持SPA开发,配合路由懒加载、代码分割等技术可进一步提升性能。


Vue3中通过VueRouter实现SPA,提供useRouter、useRoute等组合式API,支持编程导航、路由守卫等高级功能。


SPA特别适合交互密集型应用,但需根据项目需求权衡其优缺点。


关联阅读推荐

SPA 技术支撑体系详解


SPA(单页应用)详解


1. 基本概念

SPA (Single Page Application,单页应用)是一种Web应用程序架构模式 ,整个应用只有一个HTML页面,通过动态重写当前页面来与用户交互,而不是加载整个新页面。


2. 与传统多页应用(MPA)的对比

特性 SPA(单页应用) MPA(多页应用)
页面数量 单页面 多页面
页面切换 客户端路由,无刷新 整页刷新,重新加载
用户体验 流畅,类似桌面应用 传统网页体验
首屏加载 较慢(需要加载所有资源) 较快(只加载当前页面)
SEO优化 需要特殊处理(SSR/预渲染) 天然友好
开发复杂度 较高(需要前端路由) 较低
数据通信 主要通过AJAX/WebSocket 主要通过表单提交
缓存效率 高(资源只需加载一次) 低(每次刷新重新加载)

3. SPA 的工作原理

3.1 基本流程

复制代码
1. 浏览器首次请求 → 加载基础HTML、CSS、JS
2. JS框架初始化 → 监听URL变化
3. 用户点击链接 → JS阻止默认跳转行为
4. 根据路由 → 动态加载对应组件
5. 更新DOM → 只更新变化的部分
6. 更新浏览器历史 → 无需页面刷新

3.2 技术实现

javascript

复制代码
// 简化的SPA路由原理示例
class SimpleRouter {
  constructor() {
    this.routes = {}
    this.currentUrl = ''
    
    // 监听hashchange或popstate事件
    window.addEventListener('hashchange', this.refresh.bind(this))
    window.addEventListener('popstate', this.refresh.bind(this))
  }
  
  route(path, callback) {
    this.routes[path] = callback
  }
  
  refresh() {
    this.currentUrl = location.hash.slice(1) || '/'
    if (this.routes[this.currentUrl]) {
      this.routes[this.currentUrl]()  // 执行对应组件渲染
    }
  }
  
  push(path) {
    history.pushState(null, null, '#' + path)
    this.refresh()
  }
}

4. SPA 的核心特点

4.1 客户端路由

  • 前端控制路由:浏览器URL变化不向服务器请求

  • 两种模式

    复制代码
    // Hash模式:使用 # 符号
    http://example.com/#/home
    
    // History模式:需要服务器支持
    http://example.com/home

4.2 前后端分离

复制代码
传统模式:浏览器 → 服务器 → 返回完整HTML
SPA模式:浏览器 → API服务器 → 返回JSON数据 → 前端渲染

4.3 组件化开发

复制代码
<!-- Vue SPA 组件示例 -->
<template>
  <div>
    <header-component />
    <router-view />  <!-- 动态内容区域 -->
    <footer-component />
  </div>
</template>

5. SPA 的优势与劣势

✅ 优势

  1. 用户体验优秀

    • 无刷新页面切换

    • 流畅的动画过渡

    • 类似原生应用的体验

  2. 前后端分离

    • 前端专注于UI和交互

    • 后端专注于API和数据

    • 便于团队协作

  3. 性能优化

    • 资源只需加载一次

    • 局部更新,减少数据传输

    • 良好的缓存策略

  4. 开发效率

    • 组件复用

    • 状态集中管理

    • 丰富的开发生态


❌ 劣势

  1. SEO问题

    • 搜索引擎爬虫难以抓取动态内容

    • 需要SSR(服务器端渲染)或预渲染

  2. 首屏加载慢

    • 需要加载所有框架代码

    • 解决方案:代码分割、懒加载

  3. 内存管理

    • 长时间运行可能内存泄漏

    • 需要良好的组件卸载机制

  4. 浏览器兼容

    • 依赖现代JavaScript特性

    • 可能需要polyfill


6. 常见SPA框架

框架 特点 适用场景
React + React Router 灵活性高,生态丰富 大型复杂应用
Vue + Vue Router 渐进式,易上手 中小型应用,快速开发
Angular + Angular Router 完整解决方案,强类型 企业级应用
Svelte + SvelteKit 编译时优化,体积小 性能敏感型应用

7. 现代SPA架构示例

复制代码
my-spa-app/
├── public/                 # 静态资源
├── src/
│   ├── assets/            # 图片、样式等
│   ├── components/        # 公共组件
│   ├── views/             # 页面组件
│   ├── router/            # 路由配置
│   ├── store/             # 状态管理
│   ├── api/               # 接口封装
│   ├── utils/             # 工具函数
│   └── main.js            # 应用入口
├── package.json
└── vite.config.js         # 构建配置

8. 优化SPA的技巧

8.1 性能优化

复制代码
// 1. 路由懒加载
const Home = () => import('./views/Home.vue')

// 2. 组件异步加载
const HeavyComponent = defineAsyncComponent(() => 
  import('./components/HeavyComponent.vue')
)

// 3. 代码分割
// webpack/vite会自动分割不同入口的代码

8.2 SEO优化

复制代码
// 使用SSR(服务器端渲染)
// Nuxt.js (Vue) 或 Next.js (React)

// 或使用预渲染
// vite-plugin-prerender

8.3 状态保持

复制代码
// 使用vue-router的keep-alive
<router-view v-slot="{ Component }">
  <keep-alive>
    <component :is="Component" />
  </keep-alive>
</router-view>

9. 何时选择SPA?

适合SPA的场景:

  1. 交互密集型应用(如:管理后台、Dashboard)

  2. 需要类原生体验(如:Web版办公软件)

  3. 频繁交互的应用(如:在线编辑器、社交应用)

  4. 移动端Web应用(追求流畅体验)


不适合SPA的场景:

  1. 内容型网站(如:博客、新闻站)

  2. 对SEO要求高的网站

  3. 需要快速首屏加载的营销页

  4. 网络环境差的地区访问


总结

SPA是现代Web开发的主流模式 ,它通过前端路由、组件化、API通信等技术,提供了接近原生应用的体验。虽然存在SEO和首屏加载的挑战,但通过SSR、代码分割等优化手段,这些问题都能得到很好的解决。


选择是否使用SPA,需要根据项目类型、用户需求和技术团队能力综合考量。对于需要丰富交互、追求用户体验的Web应用,SPA通常是首选方案。


Vue 3 中使用 Vue Router 实现 SPA(单页应用)的方法:


1. 安装 Vue Router

bash

复制代码
npm install vue-router@4
# 或
yarn add vue-router@4

2. 基本配置

2.1 创建路由实例

复制代码
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('../views/Home.vue')
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue')
  },
  {
    path: '/user/:id',
    name: 'User',
    component: () => import('../views/User.vue'),
    props: true
  },
  {
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    component: () => import('../views/NotFound.vue')
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

2.2 挂载到 Vue 应用

复制代码
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)
app.use(router)
app.mount('#app')

3. 在组件中使用

3.1 App.vue(根组件)

复制代码
<template>
  <div id="app">
    <nav>
      <router-link to="/">首页</router-link> |
      <router-link to="/about">关于</router-link> |
      <router-link :to="{ name: 'User', params: { id: 123 }}">
        用户页面
      </router-link>
    </nav>
    
    <!-- 路由出口 -->
    <router-view v-slot="{ Component }">
      <transition name="fade" mode="out-in">
        <component :is="Component" />
      </transition>
    </router-view>
  </div>
</template>

<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
</style>

3.2 页面组件示例

复制代码
<!-- src/views/Home.vue -->
<template>
  <div class="home">
    <h1>首页</h1>
    <p>当前路由:{{ $route.path }}</p>
  </div>
</template>

<script setup>
import { useRoute } from 'vue-router'

const route = useRoute()
</script>

4. 路由导航

4.1 编程式导航

复制代码
<script setup>
import { useRouter } from 'vue-router'

const router = useRouter()

// 基本导航
const goToAbout = () => {
  router.push('/about')
}

// 命名路由
const goToUser = () => {
  router.push({ name: 'User', params: { id: 123 } })
}

// 替换当前路由
const replaceRoute = () => {
  router.replace('/about')
}

// 前进后退
const goBack = () => {
  router.go(-1)
}

// 带查询参数
const goWithQuery = () => {
  router.push({
    path: '/about',
    query: { name: 'john', age: 25 }
  })
}
</script>

5. 路由守卫

5.1 全局守卫

复制代码
// src/router/index.js
router.beforeEach((to, from, next) => {
  // 验证用户是否登录
  const isAuthenticated = checkAuth()
  
  if (to.meta.requiresAuth && !isAuthenticated) {
    next({ name: 'Login' })
  } else {
    next()
  }
})

router.afterEach((to, from) => {
  // 页面访问统计
  trackPageView(to.path)
})

5.2 路由元信息

复制代码
const routes = [
  {
    path: '/dashboard',
    component: () => import('../views/Dashboard.vue'),
    meta: {
      requiresAuth: true,
      title: '控制面板'
    }
  }
]

// 在导航守卫中使用
router.beforeEach((to, from) => {
  document.title = to.meta.title || '默认标题'
})

5.3 组件内守卫

复制代码
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

// 离开守卫
onBeforeRouteLeave((to, from) => {
  const answer = window.confirm('确定要离开吗?')
  return answer
})

// 更新守卫
onBeforeRouteUpdate(async (to, from) => {
  // 获取新的用户数据
  await fetchUserData(to.params.id)
})
</script>

6. 高级功能

6.1 嵌套路由

复制代码
const routes = [
  {
    path: '/user/:id',
    component: () => import('../views/User.vue'),
    children: [
      {
        path: '', // 默认子路由
        component: () => import('../views/UserProfile.vue')
      },
      {
        path: 'posts',
        component: () => import('../views/UserPosts.vue')
      },
      {
        path: 'settings',
        component: () => import('../views/UserSettings.vue')
      }
    ]
  }
]

vue

复制代码
<!-- User.vue -->
<template>
  <div>
    <h2>用户信息</h2>
    <router-link :to="`/user/${id}/posts`">帖子</router-link>
    <router-link :to="`/user/${id}/settings`">设置</router-link>
    
    <!-- 嵌套路由出口 -->
    <router-view />
  </div>
</template>

6.2 路由懒加载

复制代码
const routes = [
  {
    path: '/dashboard',
    component: () => import(/* webpackChunkName: "dashboard" */ '../views/Dashboard.vue')
  }
]

6.3 滚动行为

复制代码
const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else if (to.hash) {
      return {
        el: to.hash,
        behavior: 'smooth'
      }
    } else {
      return { top: 0 }
    }
  }
})

7. TypeScript 支持

复制代码
// src/router/index.ts
import { RouteRecordRaw } from 'vue-router'

declare module 'vue-router' {
  interface RouteMeta {
    requiresAuth?: boolean
    title?: string
  }
}

const routes: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'Home',
    component: () => import('../views/Home.vue')
  }
]

8. 完整示例结构

复制代码
src/
├── main.js
├── App.vue
├── router/
│   └── index.js
└── views/
    ├── Home.vue
    ├── About.vue
    ├── User.vue
    ├── Dashboard.vue
    └── NotFound.vue

这就是 Vue 3 中使用 Vue Router 实现 SPA 的基本方法。Vue Router 4 与 Vue 3 完全兼容,提供了 Composition API 支持,使得在 setup 函数中使用路由变得更加方便。


使用表格对比总结:useRouter useRoute useLink


Vue Router 组合式 API 对比表

特性 useRouter useRoute useLink
作用 路由实例对象,用于编程式导航 当前路由信息,只读响应式对象 自定义 router-link 的底层 API
返回类型 Router 对象 RouteLocationNormalizedLoaded 对象 { route, href, isActive, isExactActive, navigate }
主要功能 1. 导航 (push, replace, go) 2. 路由守卫 3. 路由管理 1. 获取当前路由信息 2. 访问路由参数、查询参数等 1. 自定义链接组件 2. 获取链接状态 3. 自定义导航行为
常用属性/方法 - router.push() - router.replace() - router.go() - router.back() - router.forward() - route.path - route.params - route.query - route.hash - route.meta - route.matched - route: 目标路由 - href: 解析后的 URL - isActive: 是否激活 - isExactActive: 是否精确激活 - navigate: 导航函数
响应式 ❌ 不是响应式对象 ✅ 是响应式对象 ✅ 返回对象中的属性是响应式的
使用场景 需要在代码中触发导航时 需要读取当前路由信息时 需要创建自定义的导航链接组件时
组合式 API ✅ 支持 ✅ 支持 ✅ 支持
选项式 API 对应 this.$router this.$route 无直接对应

详细对比

1. useRouter - 路由控制器

复制代码
import { useRouter } from 'vue-router'

const router = useRouter()

// 导航方法
router.push('/home')                    // 跳转到首页
router.push({ name: 'user', params: { id: 1 } }) // 命名路由
router.replace('/login')                // 替换当前路由
router.go(-1)                          // 后退
router.back()                          // 后退
router.forward()                       // 前进

2. useRoute - 路由信息读取器

复制代码
import { useRoute } from 'vue-router'

const route = useRoute()

// 读取路由信息(响应式)
console.log(route.path)        // 当前路径
console.log(route.params.id)   // 动态参数
console.log(route.query.page)  // 查询参数
console.log(route.hash)        // hash 值
console.log(route.meta.title)  // 元信息

// 在模板中可直接使用
const userId = computed(() => route.params.id)
复制代码
import { useLink } from 'vue-router'
import { computed } from 'vue'

// 自定义链接组件
const MyCustomLink = {
  props: {
    to: {
      type: [String, Object],
      required: true
    }
  },
  setup(props) {
    const { route, href, isActive, isExactActive, navigate } = useLink(props)
    
    // 自定义类名
    const linkClass = computed(() => ({
      'link': true,
      'active': isActive.value,
      'exact-active': isExactActive.value
    }))
    
    // 自定义点击处理
    const handleClick = (e) => {
      e.preventDefault()
      navigate()
    }
    
    return { href, linkClass, handleClick }
  },
  template: `
    <a :href="href" :class="linkClass" @click="handleClick">
      <slot />
    </a>
  `
}

使用建议总结

场景 推荐使用的 API
页面跳转、编程式导航 useRouter
获取当前路由参数/查询参数 useRoute
监听路由变化 useRoute + watch
创建自定义导航组件 useLink
简单的路由链接 <router-link>(内置组件)
需要访问路由实例进行高级操作 useRouter
需要判断当前路由状态 useRouteuseLink

组合使用示例

复制代码
import { useRouter, useRoute, watch } from 'vue-router'

export default {
  setup() {
    const router = useRouter()
    const route = useRoute()
    
    // 监听路由参数变化
    watch(
      () => route.params.id,
      (newId) => {
        if (newId) {
          fetchUserData(newId)
        }
      }
    )
    
    // 导航到其他页面
    const goToProfile = () => {
      router.push({
        name: 'profile',
        query: { tab: 'settings' }
      })
    }
    
    return { route, goToProfile }
  }
}

这三个 API 共同构成了 Vue Router 4 在 Composition API 中的核心功能,分别负责路由控制、路由信息获取和自定义链接实现。

相关推荐
console.log('npc')2 小时前
vue2 使用高德接口查询天气
前端·vue.js
天天向上10243 小时前
vue3 实现el-table 部分行不让勾选
前端·javascript·vue.js
Mr Xu_4 小时前
前端实战:基于Element Plus的CustomTable表格组件封装与应用
前端·javascript·vue.js·elementui
diygwcom6 小时前
自已学习Cursor Skills Vue实现第一个例子
前端·vue.js·学习
小马_xiaoen7 小时前
Vue3 + TS 实现一键复制指令 v-copy(优雅解决文本复制需求)
前端·javascript·vue.js
王同学 学出来7 小时前
vue复习
前端·javascript·vue.js
chenhdowue9 小时前
vue表单vxe-form如何对一个规则同时多字段联动校验,对一个控件校验多个关联字段
vue.js·vxe-table·vxe-ui
大猩猩X9 小时前
vxe-table 表格 vue 单元格渲染上传附件,显示图片列表,适配上传附件样式的用法
vue.js·vxe-table
游九尘10 小时前
Vue super flow 基于vue的流程图组件,快速易懂(Vue2)
vue.js