[vue, 指令] 链接支持在新标签页中打开

367 阅读2分钟

一、背景和思路

需求背景:用户希望能在新标签页中打开详情进行操作,操作完成后直接关闭,不用返回列表页,这样效率高。并且出于用户体验而言,所有链接都应该支持在新标签页中打开。

原生 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: 文章发布