做Vue项目时,不少同学会碰到这样的需求:想要个按钮样式的导航,点一下能跳转路由,还得有路由激活时的高亮、权限控制这些功能,这时候就得把router-link和button的用法结合起来玩出花样,今天就从基础用法到进阶场景,把vue router link button怎么用掰碎了讲,解决你写路由按钮时的疑惑~
简单说,router-link负责「路由层面的导航」(比如管理历史记录、自动高亮激活状态),button负责「UI层面的交互和样式」,只有结合起来,才能满足“按钮外观 + 路由功能 + 复杂逻辑”的需求~
`标签,既能点击跳转路由,又能继承router-link的特性:比如当前路由是`/user`时,会自动添加`router-link-active`类,用来做高亮样式。
```
这里的`isActive`是布尔值,表示当前路由是否匹配`to`属性的路径,用来手动控制激活样式,这种方式更灵活,还能给button加其他属性(disabled`、`aria-label`)。
思路2:给button加路由跳转逻辑(命令式导航)
如果跳转前要做复杂逻辑(比如弹窗确认、表单验证),更适合用<button>
+ router.push
,步骤分两步:绑定点击事件写逻辑 + 手动维护激活状态 。
举个例子:点按钮前先弹确认框,确认后跳转:
<template>
<button
@click="handleGoUser"
:class="{ active: isActive }"
class="btn-primary"
>去用户列表</button>
</template>
<script setup>
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
// 手动维护激活状态:当前路由是/user时,active类生效
const isActive = computed(() => route.path === '/user')
const handleGoUser = () => {
// 模拟弹窗确认
const isConfirm = window.confirm('确定要跳转到用户列表吗?')
if (isConfirm) {
router.push('/user') // 确认后执行路由跳转
}
}
</script>
<style scoped>
.active {
background: #67c23a; /* 激活时的高亮色 */
}
</style>
这种方式的优点 是逻辑完全自定义,比如可以在跳转前调接口、检查用户权限;缺点 是要自己写激活状态的判断(不像router-link能自动加active类)。
进阶场景:应对复杂业务的路由按钮设计
实际项目里,路由按钮不会只做“跳转”这么简单,得应对权限、多tab切换、加载状态这些需求,分享几个高频场景的解法~
场景1:带权限控制的路由按钮
删除用户」按钮只有管理员能点,普通用户只能看不能点,做法分两步:权限判断 + 渲染不同状态 。
方式1:用v-if
区分角色
<template>
<div>
<!-- 管理员可见且可点击 -->
<router-link
v-if="userRole === 'admin'"
tag="button"
to="/user/delete"
class="btn-danger"
>删除用户</router-link>
<!-- 普通用户可见但禁用 -->
<button
v-else
class="btn-danger disabled"
disabled
>删除用户</button>
</div>
</template>
<script setup>
import { computed } from 'vue'
// 假设从全局状态取用户角色(比如Vuex/Pinia)
const userRole = computed(() => store.state.user.role)
</script>
<style scoped>
.disabled {
background: #ccc;
cursor: not-allowed;
color: #999;
}
</style>
方式2:用自定义指令v-permission
(更优雅)
如果很多按钮要做权限控制,写一堆v-if
太冗余,可以封装自定义指令:
// 新建directives/permission.js
export const permission = {
mounted(el, binding) {
const { value: role } = binding
const userRole = store.state.user.role // 假设从状态管理取角色
if (role && role !== userRole) {
el.parentNode && el.parentNode.removeChild(el) // 无权限则移除元素
}
}
}
// 全局注册指令(main.js)
import { createApp } from 'vue'
import App from './App.vue'
import { permission } from './directives/permission'
const app = createApp(App)
app.directive('permission', permission)
app.mount('#app')
// 组件中使用
<router-link
v-permission="'admin'"
tag="button"
to="/user/delete"
class="btn-danger"
>删除用户</router-link>
这样只要给按钮加v-permission="'admin'"
,就能自动控制显示,代码更简洁~
场景2:多路由切换的Tab按钮组
比如页面顶部有“首页/文章/三个按钮,点击切换路由,还要高亮当前激活的按钮,这时候用router-link的active-class
属性超方便:
<template>
<div class="tab-group">
<router-link
to="/home"
tag="button"
active-class="tab-active"
class="tab-btn"
>首页</router-link>
<router-link
to="/article"
tag="button"
active-class="tab-active"
class="tab-btn"
>文章</router-link>
<router-link
to="/about"
tag="button"
active-class="tab-active"
class="tab-btn"
>lt;/router-link>
</div>
</template>
<style scoped>
.tab-btn {
padding: 8px 16px;
background: #fff;
border: 1px solid #eee;
cursor: pointer;
}
.tab-active {
border-bottom: 2px solid #42b983; /* 激活时底部下划线 */
color: #42b983;
}
</style>
Vue3用v-slot的话,还能通过isActive
手动加样式(适合更复杂的高亮逻辑):
<router-link to="/home" custom v-slot="{ navigate, isActive }">
<button
@click="navigate"
:class="[
'tab-btn',
{ 'tab-active': isActive }
]"
>首页</button>
</router-link>
场景3:带加载状态的跳转按钮
比如点按钮后要先请求接口(比如提交表单),成功后再跳转,过程中显示loading,结合async/await
和router.push
:
<template>
<button
@click="handleSubmit"
:disabled="isLoading"
class="btn-primary"
>
{{ isLoading ? '提交中...' : '提交' }}
</button>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { ref } from 'vue'
import { submitForm } from '@/api/user' // 假设的接口方法
const router = useRouter()
const isLoading = ref(false)
const handleSubmit = async () => {
isLoading.value = true
try {
// 1. 调用提交接口
await submitForm({ /* 表单数据 */ })
// 2. 接口成功后跳转
await router.push('/success')
} catch (error) {
console.error('提交失败:', error)
// 可以弹Toast提示失败
} finally {
isLoading.value = false
}
}
</script>
这种场景下用button + 命令式导航 更合适,因为要精确控制加载状态和异步逻辑的顺序~
避坑指南:这些细节决定按钮好不好用
做路由按钮时,很容易忽略可访问性、样式冲突、路由传参这些细节,踩过坑才知道多重要~
坑1:可访问性(Accessibility)没做好
按钮是用户和页面交互的核心,得考虑键盘导航 (比如用户按Tab键切换焦点,按Enter/Space触发点击)、屏幕阅读器 (比如盲人用户用读屏软件识别按钮)。
解决方法:
如果用router-link模拟button(tag="button"
或v-slot包裹button):
原生button默认支持键盘操作,所以这种方式没问题,但要给按钮加aria-label
,方便屏幕阅读器识别用途:
<router-link
tag="button"
to="/user"
aria-label="跳转到用户列表页"
class="btn-primary"
>用户页</router-link>
如果用div模拟按钮(不推荐,但设计师要特殊样式):
必须加role="button"
、tabindex="0"
,还要处理键盘事件:
<div
role="button"
tabindex="0"
@click="navigate"
@keyup.enter="navigate"
@keyup.space="navigate"
class="custom-btn"
>跳转</div>
坑2:样式冲突(尤其是Scoped CSS)
Vue单文件组件里用scoped
样式时,router-link生成的button可能吃不到样式。
<template>
<router-link tag="button" to="/user" class="btn-primary">用户页</router-link>
</template>
<style scoped>
.btn-primary {
background: blue; /* 不生效!因为router-link是组件,scoped样式不穿透 */
}
</style>
解决方法:
用深度选择器(::v-deep) 强制穿透scoped:
::v-deep .btn-primary {
background: blue;
}
把按钮样式写到全局CSS 里(比如global.css
),再在组件中引入:
<style>
@import './styles/global.css';
</style>
坑3:路由传参丢数据
用button + router.push传参时,容易搞混query
和params
,记住核心区别:
query传参 :参数跟在URL后面(比如/user?name=张三
),刷新页面参数还在,适合非敏感、需分享 的参数;
params传参 :参数藏在路由配置里(需要配合动态路由,比如/user/:id
),刷新页面会丢失,适合页面内临时传值 。
举个query
传参的例子:
// 点击按钮时传参
const handleGoUser = () => {
router.push({
path: '/user',
query: { name: '张三', age: 18 }
})
}
// 目标页面取参
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.query.name) // 张三
console.log(route.query.age) // 18
如果用params
,得先在路由配置里定义动态参数:
// router.js
const routes = [
{
path: '/user/:name', // 动态参数name
name: 'User',
component: User
}
]
// 跳转时用name + params
router.push({
name: 'User',
params: { name: '张三' }
})
// 目标页面取参
console.log(route.params.name) // 张三
刷新页面后,params
会丢失,所以要根据场景选传参方式~
对比:router-link、button、a标签该选谁?
最后聊聊这三个“导航选手”的区别,避免用错场景:
组件/标签
适用场景
优点
缺点
router-link
内部路由跳转(单页应用)
自动管理激活状态、历史记录,声明式写法简洁
样式需额外处理,复杂逻辑要结合slot
button + router.push
跳转前需复杂逻辑(弹窗、接口、权限)
完全自定义逻辑,灵活性拉满
需手动维护激活状态,代码量稍多
a标签
外部链接(跳转到其他网站)
原生支持,SEO友好
内部路由用a标签会整页刷新,破坏单页应用体验
简单总结:内部路由优先用router-link(结合button样式) ,复杂逻辑用button + router.push,外部链接用a标签~
版权声明
本文仅代表作者观点,不代表Code前端网立场。 本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。