Vue3(篇四)单页面应用到路由管理

📅 2026/7/29 2:05:43
Vue3(篇四)单页面应用到路由管理
目录前言一、SPA 与路由基础1.1 什么是 SPA1.2 路由的作用二、安装与配置2.1 安装2.2 基础配置创建路由文件挂载到应用定义路由出口三、路由模式3.1 Hash 模式3.2 History 模式推荐四、路由配置进阶4.1 动态路由参数4.2 嵌套路由4.3 重定向4.4 动态导入懒加载五、编程式导航5.1 useRouter 组合式函数5.2 导航方法对比六、路由守卫6.1 全局前置守卫6.2 路由独享守卫6.3 组件内守卫七、实战后台管理系统布局7.1 目录结构7.2 路由配置7.3 布局组件7.4 登录页八、动态路由进阶8.1 添加动态路由8.2 删除动态路由总结前言在Element Plus实战快速构建增删改查中我们用 Element Plus 实现了完整的学生管理页面。但真实的应用通常包含多个页面首页、列表页、详情页、登录页等。传统的多页面应用每次跳转都会刷新整个页面体验不流畅。现代前端应用采用SPA单页面应用架构——整个应用只有一个 HTML 页面通过 JavaScript 动态切换内容实现流畅的页面跳转效果。vue-router是 Vue 官方的路由管理器用于实现 SPA 的页面切换。本文将讲解 vue-router 的核心概念路由配置、嵌套路由、动态路由、路由守卫等。一、SPA 与路由基础1.1 什么是 SPA特性传统多页面应用MPA单页面应用SPA页面数量多个 HTML 文件一个 HTML 文件页面跳转刷新整个页面局部更新无刷新数据加载每次跳转重新加载首次加载后后续按需加载用户体验跳转时有白屏流畅类似原生应用SEO友好每个页面独立较差需要特殊处理1.2 路由的作用路由的本质是URL 与组件的映射关系URL → 组件 /students → StudentList.vue /students/1 → StudentDetail.vue /login → Login.vue类比 Java类似于 Spring MVC 的RequestMapping将 URL 映射到 Controller 方法。但前端路由不会发送请求到服务器而是在浏览器端切换组件。二、安装与配置2.1 安装npminstallvue-router4注意Vue3 对应 vue-router4Vue2 对应 vue-router3。2.2 基础配置创建路由文件新建src/router/index.tsimport{createRouter,createWebHistory}fromvue-routerimportHomefrom/views/Home.vueimportStudentsfrom/views/Students.vue// 定义路由规则constroutes[{path:/,name:Home,component:Home},{path:/students,name:Students,component:Students}]// 创建路由实例constroutercreateRouter({history:createWebHistory(),// 使用 HTML5 History 模式routes})exportdefaultrouter配置项说明pathURL 路径name路由名称可选用于编程式导航component对应的组件挂载到应用在main.ts中挂载路由import{createApp}fromvueimportAppfrom./App.vueimportrouterfrom./routerconstappcreateApp(App)app.use(router)app.mount(#app)定义路由出口在App.vue中使用router-view作为路由出口template nav router-link to/首页/router-link router-link to/students学生管理/router-link /nav !-- 路由匹配的组件将渲染在这里 -- router-view / /template组件作用router-view路由出口匹配的组件会渲染在这里router-link导航链接渲染为a标签对比router-link to/xxx类似于a href/xxx但不会刷新页面。三、路由模式vue-router 支持两种路由模式3.1 Hash 模式URL 中带有#号http://localhost:3000/#/students http://localhost:3000/#/students/1import{createRouter,createWebHashHistory}fromvue-routerconstroutercreateRouter({history:createWebHashHistory(),routes})特点不需要服务器配置支持兼容性好支持所有浏览器URL 不够美观有#3.2 History 模式推荐URL 没有#号更美观http://localhost:3000/students http://localhost:3000/students/1import{createRouter,createWebHistory}fromvue-routerconstroutercreateRouter({history:createWebHistory(),routes})特点URL 美观需要服务器配置支持否则刷新会 404服务器配置以 Nginx 为例location / { try_files $uri $uri/ /index.html; }原理所有请求都返回index.html由前端路由处理。四、路由配置进阶4.1 动态路由参数有时需要根据 ID 显示不同内容constroutes[{path:/students/:id,name:StudentDetail,component:()import(/views/StudentDetail.vue)}]在组件中获取参数script setup langts import { useRoute } from vue-router const route useRoute() // 获取路由参数 const studentId route.params.id // 获取查询参数如 /students?id1 const queryId route.query.id /script获取方式URL 示例结果route.params.id/students/123123route.query.id/students?id123123类比 Javaroute.params类似于 Spring MVC 的PathVariableroute.query类似于RequestParam。4.2 嵌套路由后台管理系统通常是布局 内容的结构侧边栏固定右侧内容根据路由切换。constroutes[{path:/,component:()import(/views/Layout.vue),children:[{path:,// 默认子路由name:Home,component:()import(/views/Home.vue)},{path:students,name:Students,component:()import(/views/Students.vue)},{path:teachers,name:Teachers,component:()import(/views/Teachers.vue)}]}]Layout.vue父组件template div classlayout aside classsidebar router-link to/首页/router-link router-link to/students学生管理/router-link router-link to/teachers教师管理/router-link /aside main classcontent !-- 子路由渲染在这里 -- router-view / /main /div /template访问/students时Layout.vue渲染Students.vue渲染在router-view /位置4.3 重定向constroutes[{path:/,redirect:/home},{path:/home,component:()import(/views/Home.vue)},{// 匹配所有未定义的路由path:/:pathMatch(.*)*,component:()import(/views/NotFound.vue)}]4.4 动态导入懒加载// 静态导入打包时会合并到一个文件importHomefrom/views/Home.vue// 动态导入按需加载单独打包constroutes[{path:/students,component:()import(/views/Students.vue)// 懒加载}]推荐除首页外的所有路由都使用动态导入减小首屏加载体积。类比 Java类似于类加载的懒加载策略用的时候才加载。五、编程式导航除了router-link还可以用 JavaScript 进行导航。5.1 useRouter 组合式函数script setup langts import { useRouter } from vue-router const router useRouter() function handleLogin() { // 编程式导航 router.push(/students) // 带参数导航 router.push({ name: StudentDetail, params: { id: 1 } }) // 带查询参数 router.push({ path: /students, query: { name: 张三 } }) } function goBack() { router.back() // 返回上一页 router.go(-1) // 同上 router.forward() // 前进 } /script template button clickhandleLogin登录后跳转/button button clickgoBack返回/button /template5.2 导航方法对比方法说明router.push(path)跳转会添加历史记录router.replace(path)跳转不会添加历史记录router.back()返回上一页router.forward()前进一页router.go(n)前进/后退 n 页类比push类似于浏览器的跳转replace类似于重定向无法后退回来。六、路由守卫路由守卫用于在路由跳转前后执行逻辑如权限验证、登录检查等。6.1 全局前置守卫// router/index.tsimport{createRouter,createWebHistory}fromvue-routerconstroutercreateRouter({// ...})// 全局前置守卫router.beforeEach((to,from,next){consttokenlocalStorage.getItem(token)// 未登录且访问非登录页if(!tokento.path!/login){next(/login)// 重定向到登录页}else{next()// 放行}})exportdefaultrouter参数说明to目标路由对象from来源路由对象next()放行next(path)重定向到指定路径类比 Java类似于 Spring MVC 的HandlerInterceptor.preHandle()。6.2 路由独享守卫constroutes[{path:/admin,component:()import(/views/Admin.vue),beforeEnter:(to,from){// 仅对此路由生效的守卫constrolelocalStorage.getItem(role)if(role!admin){return/403// 返回路径表示重定向}returntrue// 返回 true 表示放行}}]6.3 组件内守卫script setup langts import { onBeforeRouteLeave, onBeforeRouteUpdate } from vue-router // 离开前确认 onBeforeRouteLeave((to, from) { const answer window.confirm(确定要离开吗未保存的数据将丢失) if (!answer) return false // 取消导航 }) // 路由更新时动态路由参数变化 onBeforeRouteUpdate((to, from) { console.log(路由参数变化:, to.params.id) }) /script七、实战后台管理系统布局7.1 目录结构src/ ├── router/ │ └── index.ts # 路由配置 ├── views/ │ ├── Layout.vue # 布局组件 │ ├── Login.vue # 登录页 │ ├── Home.vue # 首页 │ ├── Students.vue # 学生管理 │ └── NotFound.vue # 404 页面 └── App.vue7.2 路由配置import{createRouter,createWebHistory}fromvue-routerimport{getToken}from/utils/authconstroutes[{path:/login,name:Login,component:()import(/views/Login.vue)},{path:/,component:()import(/views/Layout.vue),redirect:/home,children:[{path:home,name:Home,component:()import(/views/Home.vue),meta:{title:首页}},{path:students,name:Students,component:()import(/views/Students.vue),meta:{title:学生管理}}]},{path:/:pathMatch(.*)*,name:NotFound,component:()import(/views/NotFound.vue)}]constroutercreateRouter({history:createWebHistory(),routes})// 登录验证router.beforeEach((to,from,next){consttokengetToken()if(token){if(to.path/login){next(/home)// 已登录跳转首页}else{next()}}else{if(to.path/login){next()}else{next(/login)}}})exportdefaultrouter7.3 布局组件script setup langts import { ref } from vue import { useRoute, useRouter } from vue-router const route useRoute() const router useRouter() const menuItems [ { path: /home, title: 首页, icon: House }, { path: /students, title: 学生管理, icon: User } ] function handleLogout() { localStorage.removeItem(token) router.push(/login) } /script template div classlayout !-- 顶部导航 -- header classheader h1学生管理系统/h1 button clickhandleLogout退出登录/button /header div classmain !-- 侧边栏 -- aside classsidebar nav router-link v-foritem in menuItems :keyitem.path :toitem.path :class{ active: route.path item.path } {{ item.title }} /router-link /nav /aside !-- 内容区域 -- main classcontent router-view / /main /div /div /template style scoped .layout { height: 100vh; display: flex; flex-direction: column; } .header { height: 60px; background: #409eff; color: white; display: flex; justify-content: space-between; align-items: center; padding: 0 20px; } .main { flex: 1; display: flex; } .sidebar { width: 200px; background: #f5f5f5; } .sidebar nav { display: flex; flex-direction: column; } .sidebar a { padding: 16px; color: #333; text-decoration: none; } .sidebar a:hover, .sidebar a.active { background: #e6e6e6; } .content { flex: 1; padding: 20px; overflow: auto; } /style7.4 登录页script setup langts import { ref } from vue import { useRouter } from vue-router import { ElMessage } from element-plus import request from /api/request const router useRouter() const form ref({ username: , password: }) async function handleLogin() { try { const data await request.post(/api/login, form.value) localStorage.setItem(token, data.token) ElMessage.success(登录成功) router.push(/home) } catch (error) { ElMessage.error(登录失败) } } /script template div classlogin-container div classlogin-form h2登录/h2 el-form el-form-item el-input v-modelform.username placeholder用户名 / /el-form-item el-form-item el-input v-modelform.password typepassword placeholder密码 / /el-form-item el-form-item el-button typeprimary clickhandleLogin stylewidth: 100%登录/el-button /el-form-item /el-form /div /div /template style scoped .login-container { height: 100vh; display: flex; justify-content: center; align-items: center; background: #f0f2f5; } .login-form { width: 400px; padding: 40px; background: white; border-radius: 8px; } /style八、动态路由进阶有时需要根据用户权限动态添加路由。8.1 添加动态路由// 登录成功后asyncfunctionhandleLogin(){constdataawaitrequest.post(/api/login,form.value)// 获取用户权限constpermissionsawaitrequest.get(/api/permissions)// 根据权限添加路由if(permissions.includes(admin)){router.addRoute({path:/admin,name:Admin,component:()import(/views/Admin.vue)})}router.push(/home)}8.2 删除动态路由// 退出登录时functionhandleLogout(){// 删除动态路由router.removeRoute(Admin)// 或重置所有路由// router.replace(/login)}总结本文系统讲解了 vue-router 的核心概念概念作用关键点路由配置URL 与组件的映射path、component、children路由模式URL 格式Hash 模式带#、History 模式推荐嵌套路由布局 内容结构父组件用router-view /渲染子路由路由守卫跳转前后的拦截beforeEach全局守卫用于登录验证编程式导航JS 控制跳转router.push()、router.replace()动态路由根据权限添加路由router.addRoute()、router.removeRoute()最佳实践使用 History 模式URL 更美观除首页外所有路由使用动态导入懒加载全局前置守卫统一处理登录验证复杂布局使用嵌套路由下一步掌握了路由管理后我们需要学习跨组件的数据共享。下一篇文章将讲解 pinia 状态管理。作者小张 | 转载请注明出处