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前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。