useEventListener() composable 会把一个事件监听器绑定到某个元素、window、document 或某个组件上,并帮你打理好围绕它的一切:它会等待目标出现、跟随发生变化的目标、在你的组件被销毁时移除监听器,并在事件名或监听器选项发生变化时重新绑定。
凡是模板监听器够不着的地方都可以用它:window 和 document 上的事件、位于你模板之外的元素,或是需要根据你自己的状态来设置 capture、passive、once 选项去监听的事件。
在服务端,该 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 }
}对于 Quasar 已经帮你追踪好的状态,请改用相应的插件:AppVisibility 用于页面可见性,AppNetwork 用于在线状态,Screen 用于视口尺寸与断点。