在vue中按住ctrl键后页面跳转以新窗口形式打开

| 前端 | ctrl / vue / 跳转 | 1.8k | 3 分钟

接到一个需求:点击页面链接时按住 Ctrl 按键则以新窗口形式打开该链接,原页面不进行跳转,这个需求保证了页面跳转的灵活性。

分析功能点

  • 需要获取到当前被按下的按键;
  • 然后在链接跳转的时候判断是否以新窗口打开。

这个需求实现的重点是在何处添加判断是否以新窗口打开的代码,比如在每个链接点击的地方加入逻辑判断,这是最差的实现,因为这种方法改动点很大,而且无法自动兼容新链接的添加。然后可能想到的是拦截 $router.push 行为,或者封装一个自定义指令,再在合适的地方添加指令,这些都是不是很好的实现方式,我们应该考虑如何以最小的改动并且向后兼容,那就是在 全局的 beforeEach 路由守卫 中添加逻辑。

记录键盘的按键

我们需要维护一个记载当前按键的数据,用来帮助我们判断 Ctrl 键是否按下。

首先我们在项目中维护公共函数的地方新建一个 keyboard.js 文件。

const keys = {}

export function addKeyboardKey (e) {
  // 别用已经废弃的 keycode 字段
  keys[e.key] = true
}

export function removeKeyboardKey (e) {
  delete keys[e.key]
}

export function clearKeyboardKeys () {
    keys = {}
}

export function isCtrlPress(e) {
  return keys['Control']
}

然后寻找一个合适的地方添加键盘的事件监听,比如在 App.vuecreated 中。

// 下面只展示需求相关代码
import { removeKeyboardKey, addKeyboardKey, clearKeyboardKeys } from '@/utils/keyboard'

export default {
  created () {
    // 添加监听
    window.addEventListener('blur', clearKeyboardKeys)
    window.addEventListener('keydown', addKeyboardKey)
    window.addEventListener('keyup', removeKeyboardKey)
    // 记住添加事件和卸载事件总是成对存在
    this.$once('hook:beforeDestroy', () => {
      window.removeEventListener('blur', clearKeyboardKeys)
      window.removeEventListener('keydown', addKeyboardKey)
      window.removeEventListener('keyup', removeKeyboardKey)
    })
  }
}

至此我们已经完成了记录页面中的当前按下按键。

修改页面的跳转行为

我们在路由的全局守卫 beforeEach 中添加控制的代码。

// 下面只展示需求相关代码
import { isCtrlPress } from '@/utils/keyboard'

// 这里省略了router的创建,router是VueRouter的实例
router.beforeEach((to, from, next) => {
  // 在每次路由跳转的时候进行判断
  if (isCtrlPress()) {
    // 如果 ctrl 按键被按下,则以新窗口打开目标页面
    window.open('#' + to.fullPath)
    // 并阻止当前页面的跳转
    return next(false)
  }

  // 其他的代码,注意保证next至少和至多被调用一次
  // ...
}

现在我们就已经完成了点击页面链接时按住 Ctrl 按键则以新窗口形式打开该页面 😀。

😉😉😉😉😉😉😉😉😉😉😉😉😉😉

实际上有更加简单的方法:

// 创建一个按键事件,并判断当前ctrl是否按下
const oneKBEvent = new KeyboardEvent('')
if (oneKBEvent.ctrlKey) {
  window.open('#' + to.fullPath)
  return next(false)
}