Skip to page content

useEventListener 组合式 API
v2.34+

useEventListener() composable 会把一个事件监听器绑定到某个元素、window、document 或某个组件上,并帮你打理好围绕它的一切:它会等待目标出现、跟随发生变化的目标、在你的组件被销毁时移除监听器,并在事件名或监听器选项发生变化时重新绑定。

凡是模板监听器够不着的地方都可以用它:window 和 document 上的事件、位于你模板之外的元素,或是需要根据你自己的状态来设置 capture、passive、once 选项去监听的事件。

SSR 与 SSG

在服务端,该 composable 从不监听任何东西,但你传入的参数在服务端仍然会被求值。请通过一个 getter 函数(() => window)来引用 window 或 document,它只会在客户端运行,而不要直接把它们传进去。

在组件之外使用

该 composable 也可以在 setup() 之外调用:在 boot 文件、store 或普通模块里都行。那里没有挂载过程需要等待,所以它会立即开始监听(此时请给它一个元素、window 或 document,而不是一个 template ref),并且没有任何东西会自动停止它:用完后请调用 stopEventListener()。

语法

import { useTemplateRef } from 'vue'
import { useEventListener } from 'quasar'

setup () {
  const target = useTemplateRef('target') // an Element or a component

  const { stopEventListener } = useEventListener(
    target,            // or () => window, () => document, any EventTarget
    'keydown',         // or an Array of event names
    (evt) => {         // called with the Event
      // ...
    },
    {                  // all optional:
      capture: false,  // addEventListener() options
      passive: false,
      once: false,
      disabled: false  // pause listening
    }
  )

  // ...
}
function useEventListener<E extends Event = Event>(
  target: MaybeRefOrGetter<
    EventTarget | ComponentPublicInstance | null | undefined
  >,
  event: MaybeRefOrGetter<string | string[]>,
  handler: (evt: E) => void,
  options?: MaybeRefOrGetter<{
    capture?: boolean
    passive?: boolean
    once?: boolean
    disabled?: boolean
  }>
): {
  stopEventListener: () => void
}

target 可以是任意 EventTarget(window、document、一个元素)或一个组件实例(代表它的根元素),可以直接给出,也可以通过 Ref 或 getter 函数给出。最常见的是用 template ref:该 composable 会在你的组件挂载、ref 被填充后立即开始监听,而 null 或 undefined 的目标只是意味着暂时还没有可监听的对象。渲染 fragment(多个根节点)的组件没有可供监听的根元素。

event 是单个事件名或一个事件名数组(同一个处理函数为所有事件服务)。它同样可以是一个 Ref 或 getter,此时监听器会随之跟踪。

除非你设置了 passive,否则会沿用浏览器自身的默认行为,因此 window 或 document 上的 touchstart 或 wheel 监听器会按浏览器的意图保持为 passive。

stopEventListener() 会彻底移除监听器。你很少会需要它,因为组件销毁时该 composable 会自行停止。

运行时更改选项

target、event 和 options 都可以是普通值、Ref 或 getter 函数。普通值只读取一次。使用 Ref 或 getter 时,该 composable 会追踪它们读取的任何响应式状态,并在该状态变化时重新应用它们,因此你永远不需要调用任何东西来「更新」它:

  • 切换 disabled 会移除并重新绑定监听器
  • 把 target 指向另一个元素(或让某个 template ref 通过 v-if 发生变化)会让监听器随之移动
  • 更改事件名或 capture、passive、once 选项会用新的配置重新绑定监听器
import { ref } from 'vue'
import { useEventListener } from 'quasar'

setup () {
  const listening = ref(true)

  useEventListener(() => window, 'keydown', onKeydown, () => ({
    disabled: !listening.value
  }))

  function onKeydown (evt) { /* ... */ }

  // ...
}

示例

监听整个 document 上的 keydown 事件,并通过一个响应式选项来暂停该监听器:

<template>
  <div class="q-pa-md">
    <div class="q-gutter-sm q-mb-md">
      <q-toggle v-model="listening" label="Listen to keydown on the document" />
    </div>

    <div class="q-gutter-sm row items-center">
      <div>Press any key:</div>
      <q-badge :label="`last key: ${lastKey}`" />
      <q-badge color="secondary" :label="`count: ${count}`" />
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { useEventListener } from 'quasar'

const listening = ref(true)
const lastKey = ref('none')
const count = ref(0)

// a getter, so that the server-side of SSR/SSG never evaluates `document`
useEventListener(
  () => document,
  'keydown',
  evt => {
    lastKey.value = evt.key
    count.value++
  },
  () => ({ disabled: !listening.value })
)
</script>

从 setup 代码中监听 window 事件,而模板里没有任何节点(这里是一个针对未保存改动的 beforeunload 守卫):

import { ref } from 'vue'
import { useEventListener } from 'quasar'

setup () {
  const dirty = ref(false)

  useEventListener(() => window, 'beforeunload', evt => {
    if (dirty.value) evt.preventDefault()
  })

  return { dirty }
}
TIP

对于 Quasar 已经帮你追踪好的状态,请改用相应的插件:AppVisibility 用于页面可见性,AppNetwork 用于在线状态,Screen 用于视口尺寸与断点。