Vue Router 是啥?单页应用为啥离不开它?
做单页应用开发时,路由管理总让人头大?页面跳转逻辑乱、权限控制难、动态页面适配差……Vue Router 作为 Vue 生态里专门管路由的工具,能帮我们把这些难题逐个击破,这篇文章用问答形式,把 Vue Router 从基础到实战的知识点拆明白,新手也能跟着理清思路~
单页应用(SPA)的核心是**只加载一次页面,后续“切换页面”靠 JavaScript 动态渲染组件**,但用户体验得像多页应用一样流畅(比如刷新、前进后退正常),Vue Router 干的事,*把 URL 变化和组件渲染做精准映射**——让 URL 变了,页面组件也跟着换,同时还能管理页面状态、历史记录这些细节。举个例子:做个博客 SPA,点击“关于我”时,URL 从 变成 /about
,页面自动渲染 About 组件,要是没路由,要么页面刷新体验差,要么得自己写一堆逻辑监听 URL、手动切换组件,麻烦到哭,对比多页应用(每次跳转都刷新整页),SPA 靠路由实现“单页内的多视图切换”,既保流畅性又省资源,而 Vue Router 就是这套逻辑的“指挥官”。
从零开始,怎么给 Vue 项目配好路由?
想让路由跑起来,分安装、配规则、挂到根实例三步:
第一步:装依赖
打开终端,在 Vue 项目里执行 npm install vue-router
(如果用 Vue CLI 初始化项目时选了“路由”,这步会自动完成)。
第二步:写路由规则
新建 router/index.js
文件,导入 Vue、VueRouter 和要映射的组件,再定义路由规则:
import Vue from 'vue' import VueRouter from 'vue-router' import Home from '@/components/Home.vue' // 导入首页组件 import About from '@/components/About.vue' // 导入关于页组件 Vue.use(VueRouter) // 注册路由插件,让 Vue 能识别路由相关语法 // 定义路由规则:path 是 URL 路径,component 是对应组件 const routes = [ { path: '/', component: Home }, { path: '/about', component: About } ] // 创建路由实例,把规则传进去 const router = new VueRouter({ routes }) export default router // 导出路由实例,给 main.js 用
第三步:挂到 Vue 根实例
打开 main.js
,把路由实例注入 Vue 根实例,让所有组件都能访问路由功能:
import Vue from 'vue' import App from './App.vue' import router from './router' // 导入上面写的路由配置 new Vue({ router, // 把路由实例注入,所有组件可通过 this.$router 调用路由方法 render: h => h(App) }).$mount('#app')
在 App.vue
里加 <router-view></router-view>
(这是“路由出口”,匹配的组件会渲染到这里),再用 <router-link to="/">首页</router-link>
这类标签做导航,一个基础路由就跑通啦~
页面跳转有门道,声明式和编程式导航咋选?
页面跳转分声明式(用
声明式导航:适合模板里的“静态导航”
在模板里写导航时,用 <router-link>
最方便,比如导航栏、菜单:
<router-link to="/">首页</router-link> <router-link to="/about">lt;/router-link>
Vue 会把它渲染成 <a>
标签,点击自动跳转,还能自动加“激活样式”(配了 activeClass 的话),不用自己写点击事件。
编程式导航:适合逻辑里的“动态跳转”
在 JavaScript 逻辑里跳转(比如按钮点击后判断权限再跳),用 this.$router
的方法,最常用的是 push
:
methods: { submitForm() { // 假设表单验证通过,跳转到结果页 this.$router.push('/success') } }
除了 push
,还有 replace
(替换当前历史记录,后退时回不去)、go
(类似 history.go
,控制前进/后退步数)。
简单说:模板里的导航用声明式,逻辑里的跳转用编程式,各司其职更清爽~
动态路由、嵌套路由,复杂页面咋拆分?
实际项目里,页面不可能只有平级的“首页/关于页”,还得处理动态页面(如商品详情)和多层嵌套(如后台管理页的子页面)。
动态路由:复用组件,按参数变内容
比如电商 App 的商品详情页,每个商品 URL 是 /product/123
、/product/456
,但都用同一个 Product 组件,这时用动态段(:id)配路由:
{ path: '/product/:id', component: Product }
组件里通过 this.$route.params.id
拿到当前商品 ID,再发请求拿对应数据,这样不用给每个商品写新路由,一个组件复用到底,省代码又好维护。
嵌套路由:拆分多层页面结构
比如后台管理页面 /dashboard
,点侧边栏“订单”跳 /dashboard/order
,点“分析”跳 /dashboard/analysis
,这种“父页面套子页面”的结构,用嵌套路由:
{ path: '/dashboard', component: Dashboard, // 父组件 children: [ // 子路由数组 { path: 'order', component: Order }, { path: 'analysis', component: Analysis } ] }
父组件 Dashboard.vue
里得放 <router-view></router-view>
,子路由匹配的组件(Order、Analysis)会渲染到这里,这样页面结构分层清晰,和组件化思想一脉相承~
路由守卫是干啥的?权限控制、页面拦截全靠它?
路由守卫像“路由的门卫”,在路由跳转前、后做事情(比如权限验证、数据预加载、页面拦截),它分三类:全局守卫(管整个应用的路由)、路由独享守卫(只管某个路由)、组件内守卫(组件自己生命周期里管)。
全局守卫:router.beforeEach
最常用的是全局前置守卫 beforeEach
,跳转前触发,比如判断用户是否登录,没登录就拦截到登录页:
router.beforeEach((to, from, next) => { // to: 要跳去的路由;from: 从哪个路由来;next: 放行/跳转的函数 const isLogin = localStorage.getItem('token') // 假设用 token 判断登录 if (to.path === '/login') { next() // 去登录页,直接放行 } else { isLogin ? next() : next('/login') // 没登录就跳登录页 } })
还有全局后置守卫 afterEach
,跳转后触发,适合改页面标题、埋点统计。
路由独享守卫:beforeEnter
给单个路由配守卫,比如某个敏感页面,除全局验证外还要额外检查:
{ path: '/secret', component: Secret, beforeEnter: (to, from, next) => { // 这里写针对 /secret 的权限逻辑 const hasPermission = checkPermission() hasPermission ? next() : next('/403') } }
组件内守卫:beforeRouteEnter 等
组件里的守卫,比如进入组件前加载数据:
export default { beforeRouteEnter(to, from, next) { // 这里还拿不到 this(组件实例没创建),发请求放 next 里 axios.get('/data').then(res => { next(vm => { // vm 是组件实例 vm.data = res.data }) }) }, beforeRouteUpdate(to, from, next) { // 路由参数变了,但组件复用(如动态路由 /product/:id 从 123 变 456) this.fetchData(to.params.id) next() } }
组件内守卫能精细控制组件和路由的交互,处理数据加载、参数变化很方便~
开发时碰到路由 bug,常见坑咋避?
路由用顺了也会遇“玄学问题”,提前避坑省头发:
坑 1:history 模式下刷新页面 404
Vue Router 有 hash
(URL 带 #,如 xxx.com/#/about
)和 history
(URL 干净,如 xxx.com/about
)两种模式,用 history
时,服务器得配置 fallback(所有请求落到 index.html),否则用户刷新会因服务器找不到 /about
返回 404。
解决:nginx 加 try_files $uri $uri/ /index.html;
,或结合 Vue CLI 的 publicPath
配服务器规则,确保请求都指向 index.html。
坑 2:动态路由参数变了,组件不刷新
比如从 /product/123
跳 /product/456
,因组件复用(同个 Product 组件),Vue 不销毁重建,生命周期钩子(如 created)不执行,数据不更新。
解决:用 watch $route
或 beforeRouteUpdate
监听参数变化,重新拉数据:
watch: { '$route' (to) { this.fetchData(to.params.id) } } // 或用 beforeRouteUpdate beforeRouteUpdate(to, from, next) { this.fetchData(to.params.id) next() }
坑 3: 激活样式不生效
想让当前路由的
解决:加 exact
属性开精确匹配:
<router-link to="/" exact>首页</router-link>
这样只有 URL 完全等于 时才激活~
实战案例:用 Vue Router 搭个简易博客 SPA
光看理论不够,动手做个小项目更扎实,需求:做个博客,有首页(列表)、文章详情页、分类页,还要用路由守卫做权限控制(假设“草稿文章”需登录才能看)。
步骤 1:项目初始化 + 装路由
用 Vue CLI 新建项目,选上 Vue Router,结构里 router/index.js
配路由,views
文件夹放页面级组件(Home、Post、Category)。
步骤 2:配路由规则
const routes = [ { path: '/', component: Home }, { path: '/post/:id', component: Post, beforeEnter: (to, from, next) => { // 路由独享守卫,判断是否草稿 const post = getPostById(to.params.id) // 假设拿文章数据的函数 if (post.isDraft && !isLogin()) { next('/login') // 草稿且没登录,跳登录 } else { next() } } }, { path: '/category/:name', component: Category }, { path: '/login', component: Login } ]
步骤 3:写组件逻辑
- Home.vue:用假数据渲染文章列表,每个文章项用
<router-link :to="'/post/'+post.id">
跳详情页。 - Post.vue:通过
this.$route.params.id
拿文章 ID,发请求(或读假数据)渲染内容。 - Category.vue:通过
this.$route.params.name
拿分类名,渲染对应文章列表。
步骤 4:加全局守卫控制登录
在 router/index.js
里加全局前置守卫,判断非登录页且没 token 时跳登录:
router.beforeEach((to, from, next) => { const isLogin = localStorage.getItem('token') if (to.path !== '/login' && !isLogin) { next('/login') } else { next() } })
步骤 5:测试流程
- 访问首页,点文章跳详情:草稿文章会被拦截到登录页,登录后(存 token 到 localStorage)再跳回去能看。
- 点分类,URL 变
/category/前端
,页面渲染前端分类文章。 - 登录页输账号密码(模拟存 token),跳转后能正常访问受限页面。
这个小项目把路由配置、动态路由、守卫、导航方式全练了一遍,做完对 Vue Router 就有数啦~
Vue Router 是 SPA 开发的“导航中枢”,从基础配置到复杂场景,掌握路由映射、导航方式、守卫机制这些点,能让单页应用的页面管理更丝滑,多练案例、踩踩坑,遇到问题翻翻官方文档(Vue Router 文档讲得超细!),自然就熟了~下次做 SPA 时,路由这块心里就有底啦~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。