Vue Router是什么?怎么用?前端开发高频问题全解析
做Vue项目时,路由就像“导航员”,决定用户点按钮、输网址后该展示哪个页面,但刚上手Vue Router,不少同学会懵:“这东西到底咋配置?页面跳转咋做?遇到404咋处理?”今天用问答形式,把Vue Router从基础到实战的关键点掰碎了讲,不管是新手入门还是解决疑难杂症,看完心里有数~
Vue Router到底解决啥核心问题?
先想场景:做单页应用(SPA)时,页面切换不能像传统多页那样刷新整个页面(不然白做SPA了!),Vue Router就是干这个的——让SPA里的“页面切换”既流畅(不刷新),又能通过URL直观看到当前位置,还能实现权限控制、传参这些复杂逻辑。
举个栗子:用户点“我的订单”,页面从首页滑到订单页,URL从/home
变/order
,但浏览器没刷新,这就是Vue Router通过“前端路由”实现的,它背后维护了一套“URL路径 ↔ 组件”的映射规则,还能搞路由守卫(控制谁能进哪个页)、动态路由(比如用户ID不同,页面内容不同)这些高级操作。
零基础咋快速搭好Vue Router基础配置?
分5步走,拿Vue2项目举例(Vue3思路一样,API略有调整):
- 装依赖:终端执行
npm install vue-router@3
(Vue2配v3,Vue3配v4); - 建路由文件:src下新建router文件夹,里面建index.js;
- 写路由规则:在index.js里导入Vue、VueRouter,再定义routes数组(每个对象存path和component)。
import Vue from 'vue' import VueRouter from 'vue-router' import Home from '@/views/Home.vue' import About from '@/views/About.vue'
Vue.use(VueRouter) // 注册插件
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
]const router = new VueRouter({ routes })
export default router
- 挂到Vue根实例:打开main.js,导入router,加到
new Vue({ router, ... })
里; - 放路由出口:在App.vue里加
<router-view></router-view>
,这个标签就是“页面展示的位置”,路由匹配到哪个组件,就把组件渲染到这。
做完这步,访问时显示Home组件,访问/about
显示About组件,基础路由就跑通啦~
页面跳转有哪几种方式?该咋选?
分声明式和编程式两种,场景不同用法不同:
声明式:用<router-link>
组件
适合页面上的导航按钮、菜单这些“看得见的跳转”,不用写JS逻辑。
编程式:用this.$router
的方法
在方法里写逻辑,比如点击按钮后判断用户是否登录,再决定跳不跳:
methods: {
goOrder() {
if (this.isLogin) {
this.$router.push('/order') // 跳转到订单页,新增一条历史记录
} else {
this.$router.push('/login') // 跳登录页
}
}
}
常用的还有replace
(替换当前历史记录,比如支付成功后不想让用户返回支付页,就用replace)、go
(类似`history.go`,this.$router.go(-1)`返回上一页)。
适合需要逻辑判断、用户操作后(比如表单提交成功)的跳转。
简单跳转用声明式,复杂逻辑跳转用编程式~
动态路由和参数传递咋玩明白?
比如做“用户详情页”,每个用户ID不同,URL得是/user/123
、/user/456
这种,这时候就得用动态路由。
配置动态路由
在routes里加个带参数的path:
{ path: '/user/:id', component: User }
这里的:id
动态段”,组件里能拿到这个id。
组件内拿参数
在User组件里,通过this.$route.params.id
获取。
export default {
created() {
console.log(this.$route.params.id) // 打印当前用户ID
}
}
如果是查询参数(比如/user?name=张三
),就用this.$route.query.name
。
注意“组件复用”的坑
如果从/user/123
跳到/user/456
,因为用的同一个User组件,Vue为了性能会复用组件,created
这些钩子不会再执行!这时候数据没更新就坑了。
解决办法有俩:
- 用
watch
监听$route变化:watch: { '$route'(to, from) { // to是目标路由,from是当前路由 this.fetchData(to.params.id) // 重新发请求拿新用户数据 } }
- 用组件内守卫
beforeRouteUpdate
:beforeRouteUpdate(to, from, next) { this.id = to.params.id this.fetchData(this.id) next() // 必须调用next放行 }
嵌套路由咋设计更合理?
比如做后台管理系统,布局是“侧边栏 + 顶部栏 + 内容区”,内容区根据不同菜单切换页面,这时候就得用嵌套路由。
配置children
数组
在父路由的routes里加children,
{
path: '/admin',
component: AdminLayout, // 父组件,包含侧边栏、顶部栏
children: [
{ path: 'dashboard', component: Dashboard }, // 子路由,URL是/admin/dashboard
{ path: 'settings', component: Settings } // URL是/admin/settings
]
}
注意子路由的path不加/,会自动拼接父路由的path(比如父是/admin
,子path是dashboard
,完整URL就是/admin/dashboard
)。
父组件里放<router-view>
AdminLayout组件的模板里得有`
<template>
<div class="admin-layout">
<aside>侧边栏</aside>
<main><router-view></router-view></main> // 子组件渲染在这
</div>
</template>
这样访问/admin
时,先渲染AdminLayout,再根据子路由渲染对应的Dashboard或Settings,嵌套路由特别适合做“布局复用 + 子页面切换”的场景~
路由守卫有啥用?不同场景咋选?
路由守卫就像“门卫”,在路由跳转的各个阶段(比如跳之前、跳之后、进入组件前…)拦下来做验证、埋点、权限控制这些事,分三大类:
全局守卫:控制所有路由
比如在router/index.js里写:
router.beforeEach((to, from, next) => {
// to:要去的路由;from:从哪来的路由;next:放行/跳转到其他路由
if (to.path === '/pay' && !isLogin()) {
next('/login') // 没登录就跳登录页
} else {
next() // 放行
}
})
beforeEach
是跳转前触发,适合做全局权限验证、页面标题设置(比如to.meta.title
,设置document.title = to.meta.title
),还有afterEach
(跳转后触发,适合埋点统计)。
路由独享守卫:只拦单个路由
在routes里的某个路由对象上写beforeEnter
:
{
path: '/order',
component: Order,
beforeEnter: (to, from, next) => {
if (hasPermission()) {
next()
} else {
next('/403')
}
}
}
适合单个路由的特殊权限验证,比如订单页需要VIP权限。
组件内守卫:组件自己的“门卫”
在组件里写,
export default {
// 进入组件前触发(此时组件实例还没创建)
beforeRouteEnter(to, from, next) {
next(vm => {
// vm是组件实例,能访问this
vm.fetchData()
})
},
// 路由参数变化时触发(组件复用场景)
beforeRouteUpdate(to, from, next) {
this.id = to.params.id
next()
},
// 离开组件前触发(比如阻止表单未保存就跳转)
beforeRouteLeave(to, from, next) {
if (this.formDirty) {
if (confirm('表单没保存,确定离开?')) {
next()
} else {
next(false) // 取消跳转
}
} else {
next()
}
}
}
组件内守卫适合组件级的权限、数据预载、离开确认这些场景。
路由模式hash和history咋选?有啥坑?
Vue Router有两种路由模式,决定URL长啥样,还有兼容性:
hash模式(默认)
URL长这样:https://xxx.com/#/home
,靠URL里的后面的内容变化来实现路由,优点是兼容性好(IE8都能跑),不用后端配置;缺点是URL带,不太美观。
history模式
URL长这样:https://xxx.com/home
,用HTML5的history API(pushState
、replaceState
)实现,优点是URL干净美观,像多页应用;缺点是刷新页面会404(因为后端没配置的话,服务器不认识/home
这个路径,会返回404)。
咋选?
- 如果项目是内部系统、对URL美观要求不高,用hash模式,省心;
- 如果是面向用户的官网、需要SEO友好,选history模式,但得让后端配合:所有前端路由的请求,都重定向到
index.html
(比如Nginx配置try_files $uri $uri/ /index.html
)。
路由懒加载咋实现?能解决啥痛点?
做SPA时,所有组件打包成一个JS文件,用户打开页面得下载超大文件,首屏加载巨慢!路由懒加载就是把每个路由对应的组件,分成单独的JS文件,用户访问哪个路由,再加载对应的文件,实现“按需加载”。
实现方法:用import()
语法
把原来的静态导入(import Home from '@/views/Home.vue'
)改成动态导入:
{
path: '/',
component: () => import('@/views/Home.vue') // 打包时会把Home.vue单独拆成一个chunk
}
这样打包后,首页JS体积变小,用户打开首页时先加载小文件,等点其他路由时再加载对应组件的JS,首屏速度飞起~
注意:Vue Router官网推荐这种写法,Webpack会自动做代码分割,不用额外配置~
404页面咋配置?为啥要放最后?
用户访问不存在的路由时,要显示404页面,配置方法:
{
path: '/:pathMatch(.*)*', // Vue3写法,匹配所有未定义的路由
// Vue2写法:path: '*'
component: Error404
}
但必须把404路由放在routes数组的最后面!因为Vue Router的路由匹配是“从上到下”遍历的,前面的路由如果匹配到了,就不会往后走,要是把404放前面,不管访问啥都会匹配到404,就坑了。
举个错误例子:
const routes = [
{ path: '*', component: Error404 }, // 错误!放前面了
{ path: '/home', component: Home }
]
这时候访问/home
,会匹配到,显示404,所以正确做法是把404放最后:
const routes = [
{ path: '/home', component: Home },
{ path: '/about', component: About },
{ path: '/:pathMatch(.*)*', component: Error404 } // 正确,放最后
]
路由和Vuex需要联动吗?哪些场景要配合?
Vuex是状态管理,路由是页面导航,俩工具经常配合干活:
权限控制
比如用户登录状态存在Vuex的state里,全局守卫beforeEach
里判断:
router.beforeEach((to, from, next) => {
const isLogin = store.state.user.isLogin
if (to.meta.requiresAuth && !isLogin) {
next('/login')
} else {
next()
}
})
这里to.meta.requiresAuth
是路由元信息,用来标记哪些路由需要登录(比如订单页、个人中心)。
路由参数触发数据更新
路由参数变化时(比如用户从/user/1
跳到/user/2
),组件里通过Vuex的action更新数据:
watch: {
'$route'(to) {
this.$store.dispatch('user/fetchUser', to.params.id)
}
}
设置页面标题
路由元信息里存title,全局守卫afterEach
里设置document.title
:
router.afterEach((to) => {
document.title = to.meta.title || '默认标题'
})
这样不同路由切换时,页面标题自动变,体验更像原生App~
移动端路由切换动画咋实现?
给路由切换加动画,让页面跳转更丝滑,核心是用Vue的`
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。