一、背景和思路
需求背景:用户希望能在新标签页中打开详情进行操作,操作完成后直接关闭,不用返回列表页,这样效率高。并且出于用户体验而言,所有链接都应该支持在新标签页中打开。
原生 a 标签因为 href 属性,天生具备导航能力。导航能力是指:可以右键在新标签页中打开、支持快捷键打开(cmd/ctrl + 鼠标左键)、hover 时浏览器左下角会出现新页面链接、可以右键复制地址。
实现思路:通过指令获取绑定元素,然后用 a 标签包裹元素内容,href 值设为跳转路径,绑定 click 事件处理。
为什么不用 RouterLink ?
因为 RouterLink 只能用于模板当中,不能处理编程式导航的情况,无法在跳转前做处理。
注意:如果链接在新标签页中打开,那当前页面应该无变化,不响应跳转。所以需要在合适的时机阻止 a 标签的默认行为。
另外,可能会出现样式问题,需要覆盖 a 标签的默认样式:
a, a:link, a:visited, a:hover, a:active {
color: inherit;
text-decoration: none;
}
二、实现
app.directive('link', {
beforeMount(el, binding) {
const { path, query, beforeHandle } = binding.value;
const a = document.createElement('a');
// router 转成 url
a.href = router.resolve({ path, query }).href;
// 样式重置,防止影响挂载元素
a.style.all = 'unset';
// 把绑定元素的所有子元素塞到 a 标签中
while (el.firstChild) {
a.append(el.firstChild);
}
el.append(a);
el.addEventListener('click', (e) => {
// 跳转前的处理
beforeHandle && beforeHandle();
// 如果用户只是普通的左键点击链接,没按任何 xxxKey,就应该阻止 a 标签默认行为,由我们的 JS 去接管,自由操控跳转
// 但如果用户按了任何 xxxKey,或是点了鼠标其它键,都应该让浏览器接管后续逻辑
if (e.button != 0) return;
if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return;
// 阻止 a 标签的默认行为
e.preventDefault();
router.push({ path, query });
});
// 针对右键在新标签页中打开的情况
el.addEventListener('contextmenu', (e) => {
beforeHandle && beforeHandle();
});
},
});
三、使用
:key="scope.row.id" // 表格行必须加唯一key,这样每当表格数据变化时才会重新绑定指令,beforeHandle才能获取到正确的行数据,否则会出现数据错乱。
v-link="{
path: '/staff/detail',
query: {},
beforeHandle: () => jumpToDetailBefore(scope.row),
}"
path:跳转路径
query:url 参数
beforeHandle:跳转前的逻辑处理
另外:如果数据无法通过 query 传递,就在 beforeHandle 中用 localStorage 传递,并且在目标页面刷新(beforeunload)或离开(onUnmounted)时要清除缓存,否则始终残留。
四、最后
参考文章:cloud.tencent.com/developer/a…
2024-05-09: 更新使用方法
2024-04-08: 文章发布