一、vue-router link是干啥的?先搞懂单页应用跳转逻辑
做Vue单页应用开发时,导航跳转是绕不开的需求,用原生a标签?刷新页面体验差还容易丢状态;用vue-router的
举个直观对比:如果用原生<a href="/about">关于我们</a>
,点击后浏览器会请求新的/about页面,整个页面刷新;但用<router-link to="/about">关于我们</router-link>
,它会拦截点击事件,通过JS动态切换页面内容(其实是切换组件),浏览器地址栏变了,但页面没刷新,体验丝滑。
简单说,this.$router.push
)省心多了。
vue-router link基础用法:把to、tag、active-class玩明白
新手学
to属性:三种传值方式对应不同场景
to是必须的,告诉组件跳转到哪个路由,它有三种写法:
-
字符串形式:直接写路由路径,适合静态路由,比如跳转到“关于我们”页面:
<router-link to="/about">关于我们</router-link>
-
对象形式(带路径):当需要传查询参数(
?xxx=yyy
)或者动态路由参数时,用对象更灵活,比如跳转到文章详情,带文章ID:<router-link :to="{ path: '/article', query: { id: 123 }}">文章123</router-link>
点击后地址栏会变成
/article?id=123
,目标路由组件里用this.$route.query.id
就能拿到参数。 -
对象形式(带命名路由):如果路由配置了
name
(更推荐,避免路径硬编码),可以用name传值,比如路由配置是:{ name: 'Article', path: '/article/:id', component: Article }
那跳转时可以写:
<router-link :to="{ name: 'Article', params: { id: 123 }}">文章123</router-link>
这里用
params
传动态路由参数,地址栏会变成/article/123
,目标组件用this.$route.params.id
获取。注意:用name+params
时,不能同时写path
,否则params
会被忽略!
tag属性:让router-link渲染成任意HTML标签
默认情况下,<a>
标签,但有时候我们需要它变成按钮、列表项之类的,比如做导航栏,想让每个菜单项是<li>
标签,就可以用tag
:
<router-link to="/home" tag="li">首页</router-link>
这样渲染后是<li>首页</li>
,同时点击时依然能触发路由跳转。
active-class:给当前激活的路由加样式
导航栏需要“当前页面高亮”,这时候active-class
就派上用场,比如想让激活的链接加个红色下划线:
先在CSS里定义.active
样式:
.active { border-bottom: 2px solid red; }
然后在router-link里配置:
<router-link to="/home" active-class="active">首页</router-link>
当当前路由是/home
时,这个router-link会自动添加active
类名,样式就生效了。
小技巧:如果多个router-link都要统一active-class
,可以在路由实例里全局配置,不用每个组件重复写:
const router = new VueRouter({ routes, linkActiveClass: 'active' // 全局设置active-class })
exact:解决“模糊匹配”导致的active样式混乱
比如有个路由是(首页),另一个是/about
,当访问/about
时,的router-link也会被认为“匹配”(因为/about
包含),导致两个link都高亮,这时候加exact
属性就能精确匹配:
<router-link to="/" exact>首页</router-link>
这样只有当路由严格等于时,才会激活这个link的active样式。
vue-router link常见问题:这些坑90%的新手都踩过
学会基础用法后,实际开发中还会遇到各种“诡异”问题,跳转后页面没变化”“传参后参数丢了”“active样式死活不生效”… 逐个拆解:
问题:点击router-link没反应,地址栏也没变
排查方向:
- 路由配置错误:检查
routes
里有没有对应的path,比如想跳转到/about
,路由里是否配置了{ path: '/about', component: About }
? - to属性绑错了:如果用对象形式传参,有没有加v-bind(:)?比如写成
to="{ path: '/about' }"
会被当成字符串,正确是:to="{ path: '/about' }"
。 - 路由模式冲突:如果用了
history
模式,服务器是否配置了fallback( fallback到index.html)?否则直接访问/about
会404,导致router-link跳转也失效。
问题:动态路由传参后,目标页面拿不到参数
常见场景:用params
传动态路由参数,比如跳转到/article/123
,但Article
组件里this.$route.params.id
是undefined
。
原因+解决:
- 路由配置没写动态参数:路由里得是
path: '/article/:id'
,而不是path: '/article'
。 - 用了
path+params
:前面说过,用name
传参时才能带params
,用path
的话params
会被忽略,所以要么用name+params
,要么用path+query
。 - 组件复用导致参数没更新:如果
Article
组件是复用的(比如从/article/123
跳到/article/456
,组件实例没销毁),需要用watch
监听$route
变化:watch: { '$route'(to, from) { // 这里拿到新的to.params.id } }
问题:active-class样式不生效
可能原因:
- 路由匹配规则问题:比如父路由和子路由的link,父路由的link会因为“包含子路由路径”而一直激活,这时候给父路由的link加
exact
解决(参考前面exact
的用法)。 - 样式作用域问题:如果CSS加了
scoped
(比如单文件组件的<style scoped>
),active-class
的样式可能被限制,解决方法:要么把active样式放到全局CSS,要么用/deep/
穿透(比如>>> .active
或/deep/ .active
)。 - 全局配置和局部配置冲突:如果路由实例里全局设置了
linkActiveClass
,同时局部router-link又写了active-class
,以局部为准,要统一风格的话,建议全局配置。
问题:嵌套路由下,父路由link的active样式异常
比如有嵌套路由:
{ path: '/user', component: User, children: [ { path: 'profile', component: Profile }, { path: 'order', component: Order } ] }
当访问/user/profile
时,父路由/user
的router-link也会被激活(因为/user/profile
包含/user
),导致父link一直高亮。
解决:给父路由的router-link设置exact
?不行,因为/user/profile
的path不是/user
,这时候得用linkExactActiveClass(精确激活样式),或者在路由配置里调整匹配逻辑,更简单的是,利用vue-router的匹配逻辑:当子路由激活时,父路由的linkActiveClass
也会激活,所以如果要父link只在自身激活时高亮,需要结合exact
和子路由的匹配。
其实更简单的方法是,给父路由的router-link设置exact
,同时确保子路由的path不是以开头(避免变成根路由),或者,使用linkExactActiveClass
来区分“精确激活”和“包含激活”。
全局配置:
const router = new VueRouter({ routes, linkActiveClass: 'active', // 包含匹配时的样式 linkExactActiveClass: 'exact-active' // 精确匹配时的样式 })
然后父路由的link用exact-active
样式,子路由的link用active
样式,这样就能区分开。
问题:router-link和编程式导航(this.$router.push
)怎么配合?
有时候需要在点击router-link前做权限判断(比如判断用户是否登录),这时候可以给router-link加@click
事件,在事件里判断:
<router-link to="/profile" @click="checkAuth">个人中心</router-link>
export default { methods: { checkAuth(e) { if (!this.isLogin) { e.preventDefault(); // 阻止默认跳转行为 this.$router.push('/login'); // 跳转到登录页 } // 已登录则正常跳转,不需要处理,因为e.preventDefault()只在未登录时执行 } } }
这里要注意:router-link的点击事件中,先执行@click
,再执行跳转,所以可以通过e.preventDefault()
阻止默认跳转,再手动用编程式导航跳转。
vue-router link实战场景:解决真实项目里的导航需求
光懂理论不够,得落地到项目,分享几个常见实战场景,看完就能照搬:
场景:动态生成导航栏(从后端拿路由数据)
很多后台管理系统,导航栏是后端返回的(比如根据用户权限生成),这时候需要循环渲染router-link:
<template> <div class="sidebar"> <router-link v-for="item in menuList" :key="item.id" :to="item.to" :tag="item.tag || 'a'" > {{ item.title }} </router-link> </div> </template> <script> export default { data() { return { menuList: [] // 从接口获取,结构如:[{ id: 1, title: '首页', to: '/home', tag: 'li' }, ...] } }, created() { this.fetchMenu(); // 调用接口拿导航数据 }, methods: { fetchMenu() { // 模拟接口请求 axios.get('/api/menu').then(res => { this.menuList = res.data; }); } } } </script>
这样不管后端返回多少导航项,都能动态渲染成router-link,实现权限控制的动态导航。
场景:带权限控制的跳转(未登录不能进个人中心)
前面提过用@click
拦截,再复杂点可以封装成自定义指令或混入(mixin),但简单场景直接在组件里处理:
<router-link to="/profile" @click="handleProfileClick">个人中心</router-link>
export default { data() { return { isLogin: false // 实际项目里从vuex或cookie取 } }, methods: { handleProfileClick(e) { if (!this.isLogin) { e.preventDefault(); this.$message.warning('请先登录'); // 假设用了Element UI的提示 this.$router.push('/login'); } } } }
如果多个页面都要做权限控制,可以把这个逻辑抽到mixin里,减少重复代码。
场景:多语言路由匹配(比如/en/about
和/zh/about
)
做国际化时,路由可能带语言前缀,这时候router-link的to
需要动态拼接语言:
<template> <div class="lang-nav"> <router-link v-for="lang in langs" :key="lang" :to="`/${lang}/about`" > {{ lang === 'en' ? 'About' : '关于我们' }} </router-link> </div> </template> <script> export default { data() { return { langs: ['en', 'zh'] } } } </script>
或者更灵活的方式,在路由配置里用动态段:{ path: '/:lang/about', component: About }
,然后router-link的to
用对象形式传params
:
<router-link :to="{ name: 'About', params: { lang: 'en' }}">About</router-link>
这样不管语言怎么切换,路由匹配和跳转都能灵活处理。
场景:keep-alive下,router-link跳转后组件状态不重置
用<keep-alive>
缓存组件后,从/pageA
跳到/pageB
再跳回来,pageA
的状态还在,但有时候需要重置状态,这时候可以结合router-link的@click
,在跳转前销毁组件实例:
<router-link to="/pageB" @click="beforeLeave">去PageB</router-link>
export default { methods: { beforeLeave() { // 找到当前组件的实例,销毁它 this.$destroy(); } } }
或者在路由配置里给组件加key
,强制刷新:
<router-view :key="$route.fullPath"></router-view>
这样每次路由变化,router-view
都会重新渲染组件,状态就重置了,这和router-link的配合在于,router-link的跳转触发路由变化,从而触发router-view
的key
变化。
掌握vue-router link的核心逻辑
绕了这么多,其实
新手容易踩的坑,本质是对“路由匹配规则”“参数传递方式”“组件复用机制”理解不到位,记住这几点:
to
属性的三种写法对应不同传参场景,params
必须配合name
,query
配合path
更灵活;active-class
和exact
解决导航高亮的精准度问题;- 遇到跳转、传参、样式问题,先从路由配置、属性绑定、组件生命周期这几个角度排查;
- 实战中结合动态渲染、权限控制、国际化这些场景,才能真正把router-link用顺手。
最后提醒:vue-router的文档是最好的老师,遇到细节问题随时翻文档(比如linkActiveClass
和linkExactActiveClass
的区别),把基础逻辑吃透,再复杂的导航需求也能拆解成一个个小问题解决~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。