vue router link 怎么实现 disabled 效果
做 Vue 项目时,不少同学会碰到这样的需求:导航栏里某个路由链接暂时不能点,比如权限不够、步骤没完成,得让 <router-link> 有“禁用”的效果,但 Vue Router 的 <router-link> 组件本身没提供 disabled 属性,这该咋实现呢?下面从样式、逻辑、实际场景这些角度一步步拆解。
先搞懂 为啥没有原生 disabled
<router-link> 本质是个“路由导航组件”,核心作用是生成可跳转的链接(默认渲染成 <a> 标签)并处理路由跳转逻辑,从设计层面来说,它更关注“导航”行为,而非像 <button> 那样主打“交互状态控制”,所以官方没内置 disabled 属性,需开发者自己组合样式和逻辑来模拟禁用效果。
样式层面:让路由链接“看起来禁用”
用户首先得从视觉上感知“这个链接点不了”,所以第一步要调整样式,常见思路有这些:
改颜色、光标,模拟禁用态
给 <router-link> 加动态 class,控制颜色变灰、光标变成“不可点击”,示例:
<template>
<router-link
to="/target"
:class="{ 'disabled-style': isDisabled }"
>目标页面</router-link>
</template>
<style scoped>
.disabled-style {
color: #ccc; /* 文字变灰 */
cursor: not-allowed; /* 光标显示“不可点击” */
text-decoration: none; /* 去掉下划线(如果有) */
}
</style>
这里用 :class 动态绑定,当 isDisabled 为 true 时,就会应用 .disabled-style 的样式。
慎用 pointer-events: none
有些同学会用 CSS 的 pointer-events: none; 让元素“无视鼠标事件”,示例:
.disabled-style {
pointer-events: none;
}
这么做确实能阻止点击,但有副作用:不仅点击跳转被禁,hover 提示、右键菜单等鼠标事件也会被屏蔽,如果需求是“禁用时要弹出‘权限不足’提示”,用这个属性就实现不了——因为点击事件根本触发不了,所以这种方式适合“纯视觉禁用 + 完全不能交互”的场景,多数业务场景更推荐结合逻辑判断(而非全靠 CSS 屏蔽)。
逻辑层面:真正阻止路由跳转
只改样式还不够,得从逻辑上拦住跳转(毕竟用户按回车、或 JS 触发点击时,仍可能跳转),这时候要结合“事件拦截”处理。
拦截 的点击事件
<router-link> 是 Vue 组件,它的点击事件是原生 DOM 事件,所以要用 @click.native 监听,然后在事件里判断是否禁用,阻止默认行为,示例:
<router-link
to="/target"
:class="{ 'disabled-style': isDisabled }"
@click.native="handleClick"
>目标页面</router-link>
<script>
export default {
data() {
return {
isDisabled: true // 假设禁用状态由这个变量控制
}
},
methods: {
handleClick(event) {
if (this.isDisabled) {
event.preventDefault(); // 阻止默认跳转行为
this.$message.warning('该功能暂不可用'); // 提示(需结合 UI 库,如 Element Plus)
return;
}
// 不禁用的话,正常跳转(router-link 会自动处理 to 的跳转,无需额外操作)
}
}
}
</script>
这里的关键是 event.preventDefault()——它能阻止 <router-link> 默认的跳转逻辑,配合之前的样式,用户既能看到“禁用”的视觉,点击时也会被拦截并收到提示。
进阶:封装自定义路由链接组件
如果项目里很多地方要做“禁用路由链接”,重复写样式和事件太麻烦,可以封装一个 <MyRouterLink> 组件,把禁用逻辑和样式封装起来,示例:
<template>
<router-link
v-bind="$attrs"
:class="[$attrs.class, { 'disabled-style': disabled }]"
@click.native="handleClick"
>
<slot></slot>
</router-link>
</template>
<script>
export default {
name: 'MyRouterLink',
props: {
disabled: Boolean, // 控制是否禁用
to: [String, Object] // 继承 router-link 的 to 属性
},
methods: {
handleClick(event) {
if (this.disabled) {
event.preventDefault();
this.$emit('disabled-click'); // 向外触发事件,方便父组件加提示
return;
}
this.$emit('click', event); // 正常点击时触发原事件
}
}
}
</script>
<style scoped>
.disabled-style {
color: #ccc;
cursor: not-allowed;
}
</style>
用的时候就像这样:
<MyRouterLink to="/target" :disabled="isDisabled" @disabled-click="showTip" >目标页面</MyRouterLink>
这样封装后,所有需要“禁用路由链接”的地方,直接用 <MyRouterLink> 即可,代码复用性拉满~
业务场景实战:不同需求下的禁用策略
光讲理论不够,结合实际场景看看咋用。
场景 1:权限控制(不同角色看到不同可点击链接)
后台管理系统里,管理员能点“系统设置”,普通用户不能点,这时可以结合“用户角色”和“路由元信息”控制。
<template>
<div class="nav">
<router-link
v-for="route in routes"
:key="route.path"
:to="route.path"
:class="{ disabled: !hasPermission(route) }"
@click.native="handlePermissionClick(route)"
>{{ route.name }}</router-link>
</div>
</template>
<script>
export default {
data() {
return {
routes: [
{ path: '/home', name: '首页' },
{ path: '/setting', name: '系统设置', meta: { requiredRole: 'admin' } }
]
}
},
computed: {
userRole() {
return this.$store.state.user.role; // 假设从 Vuex 取用户角色
}
},
methods: {
hasPermission(route) {
// 路由元信息里的 requiredRole,和用户角色匹配
return this.userRole === route.meta.requiredRole;
},
handlePermissionClick(route) {
if (!this.hasPermission(route)) {
this.$message.warning('您没有该功能权限');
return false; // 阻止事件传播
}
}
}
}
</script>
<style scoped>
.nav .disabled {
color: #999;
cursor: not-allowed;
}
</style>
这里关键点:用 meta 给路由加“权限标记”,用 computed 实时取用户角色,点击时判断权限、拦截非法操作并提示。
场景 2:表单步骤导航(上一步/下一步禁用)
多步骤表单(比如注册流程),必须填完当前步骤才能点下一步,假设每一步对应不同路由(/step1、/step2、/step3)。
<template>
<div class="step-container">
<!-- 步骤 1 表单 -->
<input v-model="form.username" placeholder="用户名" />
<input v-model="form.password" type="password" placeholder="密码" />
<router-link
to="/step2"
:class="{ disabled: !isStep1Valid }"
@click.native="handleNext"
>下一步</router-link>
</div>
</template>
<script>
export default {
data() {
return {
form: { username: '', password: '' }
}
},
computed: {
isStep1Valid() {
// 验证用户名和密码是否填写
return this.form.username.trim() && this.form.password.trim();
}
},
methods: {
handleNext(event) {
if (!this.isStep1Valid) {
event.preventDefault();
this.$toast('请先填写完当前步骤表单'); // 假设用 Vant 的 toast
return;
}
// 验证通过,正常跳转(router-link 会处理 to 的跳转)
}
}
}
</script>
这种场景下,核心是“表单验证状态”和“路由跳转”的联动——只有验证通过,才允许跳转到下一步路由。
避坑指南:这些细节容易踩雷
实现过程中,有些细节没注意就会出问题,提前避坑!
@click 和 @click.native 搞混
<router-link> 是 Vue 组件,它本身的“点击事件”不是原生 DOM 事件,而是组件内部的自定义事件,所以必须加 .native 修饰符才能监听到真实的点击行为。
<!-- 错误:监听不到原生点击 --> <router-link @click="handleClick"></router-link> <!-- 正确:监听原生点击 --> <router-link @click.native="handleClick"></router-link>
pointer-events: none 导致提示弹不出
如果给 .disabled-style 加了 pointer-events: none,点击事件根本触发不了,就算写了 @click.native 也没用,所以要提示的场景,别用这个属性,改成“样式改颜色 + 光标,逻辑拦点击”的组合。
动态修改 isDisabled 后不生效
isDisabled 是通过计算属性或 Vuex 获取的,要确保它是响应式数据,如果是普通变量,Vue 监测不到变化,样式和逻辑都不会更新,解决方法:把 isDisabled 放在 data 里,或用 computed 返回状态。
同时用了 to 和手动 $router.push
有些同学为了“控制跳转”,既给 <router-link> 加了 to 属性,又在点击事件里用 this.$router.push,这样会导致跳转两次(router-link 默认跳转 + 手动 push),正确做法是:要么靠 <router-link> 的 to 自动跳转(拦截时阻止默认行为),要么不用 to、全靠手动 $router.push 控制。
实现 disabled 的核心思路
给 <router-link> 做“禁用”效果,本质是“视觉模拟 + 逻辑拦截”的组合拳:
- 样式层:用
class动态控制颜色、光标,模拟禁用的视觉状态; - 逻辑层:用
@click.native监听点击,判断禁用状态后阻止默认跳转、加提示; - 复杂场景:封装自定义组件,减少重复代码;结合路由元信息、Vuex 等做权限/步骤控制。
<router-link> 的核心是“导航”,没有现成的 disabled 属性,但通过灵活组合 Vue 的类绑定、事件处理,完全能实现各种“禁用”需求~ 下次碰到类似场景,就按这个思路拆解,准没错!
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网




发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。