Kliping
KlipingBerandaDokumentasiKomponenBlocksVariasiTemplateDice UIHooks
Memulai
  • Pengenalan
  • Instalasi
  • Kredit
State
  • useBoolean
  • useControllableValue
  • useCounter
  • useDebounce
  • useResetState
  • useThrottle
  • useToggle
Lanjutan
  • useCreation
  • useCustomCompareEffect
  • useLatest
  • useLockFn
  • useMemoizedFn
  • usePrevious
Siklus Hidup
  • useDebounceEffect
  • useDebounceFn
  • useDeepCompareEffect
  • useDeepCompareLayoutEffect
  • useEffectEvent
  • useEffectWithTarget
  • useInterval
  • useIsHydrated
  • useIsomorphicLayoutEffect
  • useMount
  • useThrottleEffect
  • useThrottleFn
  • useTimeout
  • useUnmount
  • useUpdate
  • useUpdateEffect
Peramban
  • useActiveElement
  • useBattery
  • useClickAnyWhere
  • useClickAway
  • useClipboard
  • useDocumentVisibility
  • useElementSize
  • useEventListener
  • useFps
  • useFullscreen
  • useHash
  • useHover
  • useInViewport
  • useIsMatchMedia
  • useIsOnline
  • useMouse
  • useNetwork
  • useOs
  • useScrollLock
  • useTextSelection
  • useTitle
Pengembangan
  • useWhyDidYouUpdate
Paket Luar
  • useMcp
  • useQuery
  • useStickToBottom
  • useSwr
Pustaka
  • isBrowser
  • createContext
  • bacaElemen
  • saatTerpasang

useEventListener

SebelumnyaBerikutnya

Memasang pendengar peristiwa yang ikut hidup dan mati bersama scope-nya.

Gerakkan penunjuk di sini

Posisi: belum digerakkan

Klik: 0

Ini fondasi. Sembilan composable lain dibangun di atasnya, jadi kesalahan di sini menyebar ke semuanya — dan kesalahan yang paling mahal bukan salah pasang, melainkan lupa melepas.

Tiga hal yang membedakannya dari addEventListener biasa:

  1. Targetnya boleh reaktif. useTemplateRef() masih null saat setup() berjalan dan baru terisi setelah komponennya terpasang. Karena itu pemasangannya dijalankan lewat watch, bukan sekali di onMounted — target yang berganti di tengah jalan ikut terlayani, pendengar lamanya dilepas lebih dulu.
  2. Dilepas lewat scope, bukan lewat komponen. onScopeDispose bekerja di dalam komponen maupun di dalam effectScope() lepas; onUnmounted hanya yang pertama. Composable ini dipakai composable lain, yang belum tentu dipanggil dari komponen.
  3. Aman di server. Tanpa window, watch-nya tetap jalan dan tidak menemukan apa pun untuk dipasangi. Tidak ada yang meledak.

Handler-nya dipanggil langsung, tanpa penampung apa pun. setup() berjalan sekali, jadi fungsi yang diterima di sini memang sudah yang terbaru selamanya — tidak ada nilai basi yang perlu disegarkan.

Pemasangan ​

npx shadcn-vue@latest add @kliping/use-event-listener

Perlu @kliping terdaftar di components.json project Anda. Lihat cara menyiapkannya.

Ikut terpasang sendiri: is-browser, target.

Salin dulu is-browser, target — masing-masing memuat sumbernya di halamannya sendiri — lalu tempel berkas ini.

ts
import { getCurrentScope, onScopeDispose, toValue, watch } from 'vue'
import type { MaybeRefOrGetter } from 'vue'
import { isBrowser } from '@/lib/is-browser'
import { bacaElemen } from '@/lib/target'
import type { TargetElemen } from '@/lib/target'

export interface UseEventListenerOptions {
  /** Elemen yang dipasangi. Kosongkan untuk `window`. */
  target?: TargetElemen
  capture?: boolean
  once?: boolean
  passive?: boolean
  /** Matikan sementara tanpa membongkar pemanggilannya. Boleh reaktif. */
  enable?: MaybeRefOrGetter<boolean>
}

export function useEventListener(
  eventName: string | string[],
  handler: (event: any) => void,
  options: UseEventListenerOptions = {},
) {
  const { capture, once, passive } = options
  const daftarNama = Array.isArray(eventName) ? eventName : [eventName]

  let lepas: (() => void) | undefined

  const bongkar = () => {
    lepas?.()
    lepas = undefined
  }

  const berhentiPantau = watch(
    () => [
      options.target === undefined
        ? (isBrowser ? window : undefined)
        : bacaElemen(options.target),
      toValue(options.enable ?? true),
    ] as const,

    ([elemen, aktif]) => {
      // Selalu bongkar dulu. Target yang berganti meninggalkan pendengar di
      // elemen lama kalau langkah ini dilewati, dan kebocoran seperti itu tidak
      // pernah terlihat sampai ada yang mengganti-ganti targetnya.
      bongkar()

      if (!aktif || !elemen?.addEventListener)
        return

      const pendengar = (event: Event) => handler(event)

      for (const nama of daftarNama)
        elemen.addEventListener(nama, pendengar, { capture, once, passive })

      lepas = () => {
        for (const nama of daftarNama)
          elemen.removeEventListener(nama, pendengar, { capture })
      }
    },

    // `immediate` supaya target yang sudah ada sejak awal — `window`, elemen
    // yang dioper langsung — tidak menunggu perubahan yang tidak akan datang.
    // `flush: 'post'` supaya template ref sudah terisi saat gilirannya tiba.
    { immediate: true, flush: 'post' },
  )

  const berhenti = () => {
    bongkar()
    berhentiPantau()
  }

  if (getCurrentScope())
    onScopeDispose(berhenti)

  return berhenti
}

API ​

ts
export interface UseEventListenerOptions {
  /** Elemen yang dipasangi. Kosongkan untuk `window`. */
  target?: TargetElemen
  capture?: boolean
  once?: boolean
  passive?: boolean
  /** Matikan sementara tanpa membongkar pemanggilannya. Boleh reaktif. */
  enable?: MaybeRefOrGetter<boolean>
}

export function useEventListener(
  eventName: string | string[],
  handler: (event: any) => void,
  options: UseEventListenerOptions = {},
)
useElementSizeuseFps

Di halaman ini

PemasanganAPI
Dirilis di bawah lisensi MIT. Ditulis ulang untuk Vue dari shadcn-vue, shadcn-ui-blocks, dan shadcn-hooks. Hak cipta © 2026 Kliping