最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue事件綁定完全指南之官方文檔未詳述的事件大全

 更新時(shí)間:2026年05月19日 10:42:47   作者:來碗疙瘩湯  
對(duì)應(yīng)Vue,個(gè)人覺得強(qiáng)大便捷功事件綁定了,這篇文章主要介紹了Vue事件綁定完全指南之官方文檔未詳述的事件大全,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

Vue 的 v-on(即 @)本質(zhì)上是對(duì)原生 DOM 事件的直接透?jìng)鳎?strong>凡是瀏覽器原生支持的事件,Vue 均可綁定。本文系統(tǒng)整理官方文檔未重點(diǎn)列出、但實(shí)際開發(fā)中極具價(jià)值的各類事件,并附帶修飾符組合、編譯器處理機(jī)制與架構(gòu)層面的深度說明。

一、編譯器如何處理事件名

理解 Vue 如何處理事件名,是掌握所有事件綁定的基礎(chǔ)。

1.1 事件名轉(zhuǎn)換規(guī)則

Vue 編譯器(@vue/compiler-dom)在處理 @eventName 時(shí)執(zhí)行以下轉(zhuǎn)換:

模板寫法          →    編譯產(chǎn)物(props key)
@click            →    onClick
@dblclick         →    onDblclick
@mouseenter       →    onMouseenter
@keydown.enter    →    onKeydown(運(yùn)行時(shí)過濾 key)
@update:modelValue →   onUpdate:modelValue

核心轉(zhuǎn)換函數(shù)來自 @vue/shared

import { toHandlerKey, camelize } from '@vue/shared'

toHandlerKey('click')       // 'onClick'
toHandlerKey('dblclick')    // 'onDblclick'
toHandlerKey(camelize('my-event')) // 'onMyEvent'

1.2 事件名大小寫規(guī)則

<!-- 以下三種寫法在組件上等價(jià)(Vue 自動(dòng)轉(zhuǎn)換) -->
@myEvent="handler"
@my-event="handler"
@my_event="handler"

<!-- 原生 DOM 元素上:保持原始大小寫,不做轉(zhuǎn)換 -->
<div @dblclick="handler" />   <!-- ? 正確 -->
<div @DblClick="handler" />   <!-- ? 無法觸發(fā)(原生事件名全小寫) -->

二、鼠標(biāo)事件(Mouse Events)

2.1 文檔常見事件

@click        <!-- 單擊 -->
@mouseover    <!-- 鼠標(biāo)懸入(含子元素冒泡) -->
@mouseout     <!-- 鼠標(biāo)離開(含子元素冒泡) -->
@mousemove    <!-- 鼠標(biāo)移動(dòng) -->
@mousedown    <!-- 鼠標(biāo)按下 -->
@mouseup      <!-- 鼠標(biāo)抬起 -->
@contextmenu  <!-- 右鍵菜單 -->

2.2 文檔未重點(diǎn)說明的鼠標(biāo)事件

@dblclick— 雙擊

<div @dblclick="onDblClick">雙擊我</div>
function onDblClick(event: MouseEvent) {
  // event.detail === 2,表示這是第二次點(diǎn)擊
  console.log('雙擊坐標(biāo):', event.clientX, event.clientY)
}

注意@click@dblclick 同時(shí)綁定時(shí),雙擊會(huì)先觸發(fā)兩次 click 再觸發(fā)一次 dblclick。如需互斥,需手動(dòng)用定時(shí)器處理:

let clickTimer: ReturnType<typeof setTimeout> | null = null

function handleClick() {
  if (clickTimer) return // 等待期間忽略單擊
  clickTimer = setTimeout(() => {
    clickTimer = null
    doSingleClick()
  }, 250)
}

function handleDblClick() {
  if (clickTimer) {
    clearTimeout(clickTimer)
    clickTimer = null
  }
  doDblClick()
}

@mouseenter/@mouseleave— 不冒泡的懸停事件

<!-- mouseover/mouseout 會(huì)因子元素冒泡頻繁觸發(fā) -->
<!-- mouseenter/mouseleave 只在進(jìn)入/離開元素本身時(shí)觸發(fā)一次 -->
<div
  @mouseenter="isHovered = true"
  @mouseleave="isHovered = false"
  :class="{ hovered: isHovered }"
>
  懸停區(qū)域(推薦用這對(duì),而非 mouseover/mouseout)
</div>

架構(gòu)建議:絕大多數(shù)懸停交互應(yīng)使用 mouseenter/mouseleave 而非 mouseover/mouseout,后者在子元素多的情況下會(huì)產(chǎn)生大量無意義的事件觸發(fā)。

@auxclick— 非主鍵點(diǎn)擊(中鍵/側(cè)鍵)

<!-- 鼠標(biāo)中鍵或其他非左鍵的點(diǎn)擊 -->
<a @auxclick.prevent="openInNewTab(url)">中鍵點(diǎn)擊新標(biāo)簽打開</a>
function onAuxClick(event: MouseEvent) {
  if (event.button === 1) {
    // button 1 = 中鍵
    event.preventDefault() // 阻止中鍵默認(rèn)的自動(dòng)滾動(dòng)
    window.open(url, '_blank')
  }
}

@click的button屬性區(qū)分左中右鍵

<div @mousedown="handleMouseDown">多鍵區(qū)域</div>
function handleMouseDown(event: MouseEvent) {
  switch (event.button) {
    case 0: console.log('左鍵'); break
    case 1: console.log('中鍵'); break
    case 2: console.log('右鍵(通常觸發(fā) contextmenu)'); break
    case 3: console.log('側(cè)鍵后退'); break
    case 4: console.log('側(cè)鍵前進(jìn)'); break
  }
}

@click的鼠標(biāo)按鍵修飾符

<!-- Vue 內(nèi)置的鼠標(biāo)按鍵修飾符(文檔有但常被忽視) -->
<div @click.left="onLeft">僅左鍵觸發(fā)</div>
<div @click.right="onRight">僅右鍵觸發(fā)(不顯示系統(tǒng)菜單)</div>
<div @click.middle="onMiddle">僅中鍵觸發(fā)</div>

三、鍵盤事件(Keyboard Events)

3.1 基礎(chǔ)事件

@keydown   <!-- 按鍵按下(持續(xù)觸發(fā)) -->
@keyup     <!-- 按鍵抬起 -->
@keypress  <!-- ?? 已廢棄,不推薦使用 -->

3.2 按鍵修飾符完整列表

Vue 內(nèi)置了以下按鍵別名,但文檔中許多組合從未被舉例

<!-- 字母數(shù)字類 -->
@keydown.enter="submit"
@keydown.tab="onTab"
@keydown.delete="onDelete"     <!-- 同時(shí)匹配 Delete 和 Backspace -->
@keydown.esc="onEsc"           <!-- 等價(jià)于 Escape -->
@keydown.space="onSpace"
@keydown.up="moveUp"
@keydown.down="moveDown"
@keydown.left="moveLeft"
@keydown.right="moveRight"

<!-- 功能鍵修飾符(可組合) -->
@keydown.ctrl.enter="submitForm"    <!-- Ctrl + Enter -->
@keydown.shift.enter="newline"      <!-- Shift + Enter -->
@keydown.alt.left="goBack"          <!-- Alt + ← -->
@keydown.meta.k="openCommandPalette" <!-- Cmd/Win + K -->
@keydown.ctrl.shift.z="redo"        <!-- Ctrl + Shift + Z -->

<!-- 直接使用 KeyboardEvent.key 值(kebab-case) -->
@keydown.page-up="scrollUp"
@keydown.page-down="scrollDown"
@keydown.home="gotoTop"
@keydown.end="gotoBottom"
@keydown.f1="showHelp"
@keydown.f11="toggleFullscreen"

<!-- 精確修飾符:只匹配完全一致的按鍵組合 -->
@keydown.exact.ctrl.a="selectAll"   <!-- 只有 Ctrl+A,有其他鍵不觸發(fā) -->

3.3 自定義按鍵別名(Vue 2 遺留,Vue 3 移除)

Vue 3 已移除 Vue.config.keyCodes,推薦直接使用 KeyboardEvent.key 的 kebab-case 形式:

<!-- Vue 3 直接使用原生 key 名 -->
@keydown.arrow-up="moveUp"
@keydown.home="gotoTop"
@keydown.escape="close"
@keydown.tab="focusNext"

3.4 IME 輸入法相關(guān)事件(極重要但完全未文檔化)

<!-- compositionstart: 開始輸入法輸入(中日韓輸入時(shí)) -->
<!-- compositionupdate: 輸入法候選字更新 -->
<!-- compositionend: 輸入法輸入完成,確認(rèn)上屏 -->
<input
  @compositionstart="isComposing = true"
  @compositionupdate="onCompositionUpdate"
  @compositionend="isComposing = false; onInput($event)"
  @input="!isComposing && onInput($event)"
/>
const isComposing = ref(false)

// 正確處理中文輸入的搜索框
function onInput(event: Event) {
  if (isComposing.value) return // 輸入法選字過程中不觸發(fā)搜索
  const value = (event.target as HTMLInputElement).value
  doSearch(value)
}

這是處理中文/日文/韓文輸入時(shí)最常見的坑,v-model 內(nèi)部已處理此問題,但手動(dòng)監(jiān)聽 @input 時(shí)必須自行處理。

四、表單與輸入事件(Form & Input Events)

4.1@inputvs@change的區(qū)別

<!-- @input:每次值變化立即觸發(fā)(包括輸入法過程中) -->
<input @input="onInput" />

<!-- @change:失去焦點(diǎn)且值有變化時(shí)觸發(fā)(或按 Enter) -->
<input @change="onChange" />

<!-- select 元素 -->
<select @change="onSelect">
  <option>A</option>
</select>

<!-- checkbox/radio -->
<input type="checkbox" @change="onCheckboxChange" />

4.2 焦點(diǎn)事件

@focus     <!-- 獲得焦點(diǎn)(不冒泡) -->
@blur      <!-- 失去焦點(diǎn)(不冒泡) -->
@focusin   <!-- 獲得焦點(diǎn)(冒泡,可在父元素監(jiān)聽子元素焦點(diǎn)) -->
@focusout  <!-- 失去焦點(diǎn)(冒泡) -->
<!-- 實(shí)用場(chǎng)景:監(jiān)聽整個(gè)表單區(qū)域內(nèi)的焦點(diǎn)狀態(tài) -->
<div
  @focusin="isFormActive = true"
  @focusout="isFormActive = false"
>
  <input placeholder="字段1" />
  <input placeholder="字段2" />
</div>

focusin/focusout 會(huì)冒泡,而 focus/blur 不會(huì)——這是在父容器層面感知子元素焦點(diǎn)的標(biāo)準(zhǔn)方式。

4.3@reset/@submit

<!-- 原生 form 事件 -->
<form
  @submit.prevent="handleSubmit"
  @reset="handleReset"
>
  <input type="text" />
  <button type="submit">提交</button>
  <button type="reset">重置</button>
</form>

4.4@invalid— 原生表單驗(yàn)證失敗

<!-- 原生 HTML5 表單驗(yàn)證失敗時(shí)觸發(fā) -->
<input
  type="email"
  required
  @invalid.prevent="showCustomError($event)"
/>
function showCustomError(event: Event) {
  const input = event.target as HTMLInputElement
  // 阻止瀏覽器默認(rèn)氣泡提示,使用自定義 UI
  input.setCustomValidity('請(qǐng)輸入有效的郵箱地址')
}

4.5@select— 文本選中

<!-- 用戶在 input/textarea 中選中文字時(shí)觸發(fā) -->
<textarea
  @select="onTextSelect"
/>
function onTextSelect(event: Event) {
  const textarea = event.target as HTMLTextAreaElement
  const selectedText = textarea.value.slice(
    textarea.selectionStart!,
    textarea.selectionEnd!
  )
  console.log('選中的文字:', selectedText)
}

五、拖拽事件(Drag & Drop Events)

這是文檔中完全沒有專門章節(jié)講解的一類事件,但實(shí)現(xiàn)原生拖拽時(shí)必須用到。

<!-- 被拖拽的元素上 -->
<div
  draggable="true"
  @dragstart="onDragStart"    <!-- 開始拖拽 -->
  @drag="onDragging"          <!-- 拖拽中(持續(xù)觸發(fā)) -->
  @dragend="onDragEnd"        <!-- 拖拽結(jié)束(無論是否成功) -->
>
  拖我
</div>

<!-- 放置目標(biāo)上 -->
<div
  @dragenter="onDragEnter"    <!-- 拖拽元素進(jìn)入目標(biāo)區(qū)域 -->
  @dragover.prevent="onDragOver"  <!-- 必須 prevent 才能觸發(fā) drop! -->
  @dragleave="onDragLeave"    <!-- 拖拽元素離開目標(biāo)區(qū)域 -->
  @drop.prevent="onDrop"      <!-- 放置 -->
>
  放這里
</div>
function onDragStart(event: DragEvent) {
  event.dataTransfer!.setData('text/plain', 'payload-data')
  event.dataTransfer!.effectAllowed = 'move' // 'copy' | 'move' | 'link'
}

function onDrop(event: DragEvent) {
  const data = event.dataTransfer!.getData('text/plain')
  console.log('接收到:', data)
}

@dragover.prevent 是關(guān)鍵:不調(diào)用 preventDefault() 的話,@drop 事件永遠(yuǎn)不會(huì)觸發(fā)。

5.1 文件拖拽上傳

<div
  @dragover.prevent
  @drop.prevent="onFileDrop"
  class="upload-zone"
>
  拖拽文件到此處上傳
</div>
function onFileDrop(event: DragEvent) {
  const files = Array.from(event.dataTransfer!.files)
  files.forEach(file => {
    console.log('文件名:', file.name, '大?。?, file.size)
    uploadFile(file)
  })
}

六、剪貼板事件(Clipboard Events)

<input
  @cut="onCut"
  @copy="onCopy"
  @paste="onPaste"
/>
function onCopy(event: ClipboardEvent) {
  // 修改復(fù)制的內(nèi)容
  event.preventDefault()
  const selection = window.getSelection()?.toString() ?? ''
  event.clipboardData!.setData('text/plain', `${selection} —— 來自我的網(wǎng)站`)
}

function onPaste(event: ClipboardEvent) {
  event.preventDefault()
  const text = event.clipboardData!.getData('text/plain')
  // 過濾內(nèi)容后再插入
  const sanitized = sanitizeHtml(text)
  document.execCommand('insertText', false, sanitized)
}

// 讀取粘貼的文件(如截圖)
function onPasteWithFile(event: ClipboardEvent) {
  const items = Array.from(event.clipboardData!.items)
  const imageItem = items.find(item => item.type.startsWith('image/'))
  if (imageItem) {
    const file = imageItem.getAsFile()!
    uploadImage(file)
  }
}

七、滾動(dòng)與視口事件

7.1@scroll

<!-- 元素滾動(dòng)(注意:不冒泡,需綁在實(shí)際滾動(dòng)的元素上) -->
<div class="scroll-container" @scroll="onScroll">
  <div class="content">長(zhǎng)內(nèi)容...</div>
</div>
function onScroll(event: Event) {
  const el = event.target as HTMLElement
  const { scrollTop, scrollHeight, clientHeight } = el
  // 判斷是否滾動(dòng)到底部
  if (scrollTop + clientHeight >= scrollHeight - 10) {
    loadMore()
  }
}

7.2@wheel— 鼠標(biāo)滾輪

<!-- wheel 比 scroll 更底層,可以阻止?jié)L動(dòng) -->
<div @wheel.prevent="onWheel">阻止此區(qū)域的頁(yè)面滾動(dòng)</div>
function onWheel(event: WheelEvent) {
  event.deltaY  // 垂直滾動(dòng)量(正值向下,負(fù)值向上)
  event.deltaX  // 水平滾動(dòng)量
  event.deltaMode // 0=像素, 1=行, 2=頁(yè)
}

@scroll 無法 preventDefault(),@wheel 可以——兩者用途不同。

八、觸摸事件(Touch Events)

<div
  @touchstart="onTouchStart"   <!-- 手指接觸屏幕 -->
  @touchmove.prevent="onTouchMove"  <!-- 手指移動(dòng)(prevent 阻止頁(yè)面滾動(dòng)) -->
  @touchend="onTouchEnd"       <!-- 手指抬起 -->
  @touchcancel="onTouchCancel" <!-- 觸摸被中斷(來電等) -->
>
  觸摸區(qū)域
</div>
let startX = 0
let startY = 0

function onTouchStart(event: TouchEvent) {
  startX = event.touches[0].clientX
  startY = event.touches[0].clientY
}

function onTouchEnd(event: TouchEvent) {
  const endX = event.changedTouches[0].clientX
  const endY = event.changedTouches[0].clientY
  const dx = endX - startX
  const dy = endY - startY

  // 簡(jiǎn)單滑動(dòng)手勢(shì)識(shí)別
  if (Math.abs(dx) > Math.abs(dy)) {
    dx > 0 ? emit('swipe-right') : emit('swipe-left')
  } else {
    dy > 0 ? emit('swipe-down') : emit('swipe-up')
  }
}

8.1 指針事件(Pointer Events)— 統(tǒng)一鼠標(biāo)+觸摸+筆

<!-- PointerEvent 是鼠標(biāo)事件 + 觸摸事件的統(tǒng)一抽象(推薦使用) -->
<div
  @pointerdown="onPointerDown"
  @pointermove="onPointerMove"
  @pointerup="onPointerUp"
  @pointerenter="onPointerEnter"
  @pointerleave="onPointerLeave"
  @pointercancel="onPointerCancel"
  @gotpointercapture="onGotCapture"
  @lostpointercapture="onLostCapture"
>
</div>
function onPointerDown(event: PointerEvent) {
  event.pointerId    // 指針 ID(多點(diǎn)觸控時(shí)區(qū)分不同手指)
  event.pointerType  // 'mouse' | 'pen' | 'touch'
  event.pressure     // 壓力值 0-1(筆/觸控板支持)
  event.tiltX        // 筆的傾斜角
  event.isPrimary    // 是否是主指針(多點(diǎn)觸控時(shí)第一個(gè)接觸的手指)
  
  // 捕獲指針:即使鼠標(biāo)移出元素也繼續(xù)接收事件
  ;(event.target as HTMLElement).setPointerCapture(event.pointerId)
}

架構(gòu)建議:新項(xiàng)目的拖拽、繪圖、手勢(shì)等功能優(yōu)先使用 Pointer Events,而非分別處理 Mouse Events 和 Touch Events。**

九、媒體事件(Media Events)

這是文檔完全沒有覆蓋的一類,對(duì)音視頻開發(fā)至關(guān)重要。

<video
  ref="videoRef"
  @loadstart="onLoadStart"       <!-- 開始加載 -->
  @loadedmetadata="onMetaLoaded" <!-- 元數(shù)據(jù)加載完成(可獲取時(shí)長(zhǎng)、尺寸) -->
  @loadeddata="onDataLoaded"     <!-- 當(dāng)前幀數(shù)據(jù)加載完成 -->
  @canplay="onCanPlay"           <!-- 可以開始播放 -->
  @canplaythrough="onCanPlayThrough" <!-- 預(yù)計(jì)不會(huì)緩沖中斷地播放到結(jié)束 -->
  @play="onPlay"                 <!-- 開始播放 -->
  @playing="onPlaying"           <!-- 在緩沖后恢復(fù)播放 -->
  @pause="onPause"               <!-- 暫停 -->
  @ended="onEnded"               <!-- 播放結(jié)束 -->
  @timeupdate="onTimeUpdate"     <!-- 播放進(jìn)度更新(約每秒 4-66 次) -->
  @seeking="onSeeking"           <!-- 用戶拖拽進(jìn)度條中 -->
  @seeked="onSeeked"             <!-- 拖拽完成 -->
  @waiting="onWaiting"           <!-- 緩沖中,等待數(shù)據(jù) -->
  @stalled="onStalled"           <!-- 瀏覽器嘗試獲取數(shù)據(jù)但暫停 -->
  @error="onError"               <!-- 加載/播放錯(cuò)誤 -->
  @volumechange="onVolumeChange" <!-- 音量或靜音狀態(tài)變化 -->
  @ratechange="onRateChange"     <!-- 播放速率變化 -->
  @durationchange="onDurChange"  <!-- 時(shí)長(zhǎng)變化 -->
  @progress="onProgress"         <!-- 下載進(jìn)度更新 -->
  @suspend="onSuspend"           <!-- 瀏覽器停止加載(數(shù)據(jù)已足夠) -->
  @abort="onAbort"               <!-- 資源加載中止(非錯(cuò)誤) -->
  @emptied="onEmptied"           <!-- 媒體資源被清空 -->
>
</video>
function onTimeUpdate(event: Event) {
  const video = event.target as HTMLVideoElement
  const progress = (video.currentTime / video.duration) * 100
  progressBar.value = progress
}

function onMetaLoaded(event: Event) {
  const video = event.target as HTMLVideoElement
  console.log('視頻時(shí)長(zhǎng):', video.duration)
  console.log('視頻尺寸:', video.videoWidth, 'x', video.videoHeight)
}

十、窗口與文檔事件(配合useEventListener)

這類事件不能直接在 Vue 模板中用 @ 綁定,需要在 onMounted 中手動(dòng)注冊(cè),或使用 VueUse 的 useEventListener

import { onMounted, onUnmounted } from 'vue'

// 方式一:手動(dòng)管理
onMounted(() => {
  window.addEventListener('resize', onResize)
  window.addEventListener('online', onOnline)
  window.addEventListener('offline', onOffline)
  document.addEventListener('visibilitychange', onVisibilityChange)
})
onUnmounted(() => {
  window.removeEventListener('resize', onResize)
  // ...
})

// 方式二:VueUse(推薦)
import { useEventListener } from '@vueuse/core'
useEventListener(window, 'resize', onResize)
useEventListener(document, 'visibilitychange', onVisibilityChange)

常見窗口級(jí)事件清單

// 視口
window.addEventListener('resize', handler)          // 窗口大小變化
window.addEventListener('scroll', handler)          // 窗口滾動(dòng)
window.addEventListener('orientationchange', handler) // 屏幕旋轉(zhuǎn)

// 網(wǎng)絡(luò)狀態(tài)
window.addEventListener('online', handler)          // 網(wǎng)絡(luò)恢復(fù)
window.addEventListener('offline', handler)         // 網(wǎng)絡(luò)斷開

// 頁(yè)面生命周期
window.addEventListener('load', handler)            // 頁(yè)面全部資源加載完畢
window.addEventListener('beforeunload', handler)    // 頁(yè)面即將卸載(可彈確認(rèn)框)
window.addEventListener('unload', handler)          // 頁(yè)面卸載中
window.addEventListener('pagehide', handler)        // 頁(yè)面隱藏(bfcache 友好)
window.addEventListener('pageshow', handler)        // 頁(yè)面顯示

// 歷史記錄
window.addEventListener('popstate', handler)        // 瀏覽器前進(jìn)/后退
window.addEventListener('hashchange', handler)      // hash 變化

// 焦點(diǎn)
window.addEventListener('focus', handler)           // 窗口獲得焦點(diǎn)
window.addEventListener('blur', handler)            // 窗口失去焦點(diǎn)

// 文檔可見性
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
    pauseVideo()  // 切換標(biāo)簽頁(yè)時(shí)暫停視頻
  }
})

// 存儲(chǔ)
window.addEventListener('storage', (event: StorageEvent) => {
  // 跨標(biāo)簽頁(yè)的 localStorage 變化通知
  console.log(event.key, event.oldValue, event.newValue)
})

// 全屏
document.addEventListener('fullscreenchange', handler)

// 打印
window.addEventListener('beforeprint', handler)
window.addEventListener('afterprint', handler)

// 游戲手柄
window.addEventListener('gamepadconnected', handler)
window.addEventListener('gamepaddisconnected', handler)

十一、Intersection Observer 事件(元素進(jìn)入視口)

這不是傳統(tǒng) DOM 事件,但在 Vue 中有標(biāo)準(zhǔn)的封裝方式,常用于懶加載:

// 自定義指令實(shí)現(xiàn) v-intersect
const vIntersect = {
  mounted(el: HTMLElement, binding: DirectiveBinding) {
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            binding.value(entry)
            if (binding.modifiers.once) {
              observer.unobserve(el)
            }
          }
        })
      },
      {
        threshold: binding.arg ? parseFloat(binding.arg as string) : 0,
        rootMargin: '0px 0px -50px 0px'
      }
    )
    observer.observe(el)
    ;(el as any).__intersectObserver__ = observer
  },
  unmounted(el: HTMLElement) {
    ;(el as any).__intersectObserver__?.disconnect()
  }
}

// 使用
app.directive('intersect', vIntersect)
<img
  v-intersect.once="onVisible"
  :src="placeholder"
  data-src="real-image.jpg"
/>

十二、自定義組件事件

12.1defineEmits完整用法

// 帶類型驗(yàn)證(運(yùn)行時(shí) + 編譯時(shí)雙重驗(yàn)證)
const emit = defineEmits<{
  change: [value: string]                      // 單參數(shù)
  update: [id: number, value: string]          // 多參數(shù)
  'node:select': [node: TreeNode]              // 帶命名空間的事件名
  submit: [data: FormData, reset: () => void]  // 包含回調(diào)函數(shù)參數(shù)
}>()

// 或帶運(yùn)行時(shí)驗(yàn)證的對(duì)象語(yǔ)法
const emit = defineEmits({
  change: (value: string) => {
    // 返回 false 表示驗(yàn)證失敗,控制臺(tái)輸出警告
    return typeof value === 'string' && value.length > 0
  },
  submit: null // null 表示不驗(yàn)證
})

12.2v-model多綁定(Vue 3.4+)

<!-- 父組件 -->
<MyForm
  v-model:title="title"
  v-model:content="content"
  v-model:tags="tags"
/>
// 子組件
const props = defineProps<{
  title: string
  content: string
  tags: string[]
}>()

const emit = defineEmits<{
  'update:title': [value: string]
  'update:content': [value: string]
  'update:tags': [value: string[]]
}>()

12.3v-model修飾符透?jìng)?/h3>
<!-- 父 -->
<MyInput v-model.trim.uppercase="text" />
// 子組件處理修飾符
const props = defineProps<{
  modelValue: string
  modelModifiers?: {
    trim?: boolean
    uppercase?: boolean
  }
}>()

const emit = defineEmits<{
  'update:modelValue': [value: string]
}>()

function onInput(event: Event) {
  let value = (event.target as HTMLInputElement).value
  if (props.modelModifiers?.trim) value = value.trim()
  if (props.modelModifiers?.uppercase) value = value.toUpperCase()
  emit('update:modelValue', value)
}

十三、修飾符組合速查表

事件修飾符(可鏈?zhǔn)浇M合):
.stop       → event.stopPropagation()
.prevent    → event.preventDefault()
.self       → 只有 event.target === 當(dāng)前元素時(shí)觸發(fā)
.capture    → 使用捕獲模式(從上到下,而非冒泡)
.once       → 只觸發(fā)一次,之后自動(dòng)移除監(jiān)聽
.passive    → 告知瀏覽器不調(diào)用 preventDefault(scroll 性能優(yōu)化)
.native     → ?? Vue 3 已移除,請(qǐng)用 v-bind="$attrs" 透?jìng)?
.exact      → 精確匹配按鍵組合,不允許其他修飾鍵

實(shí)用組合:
@click.stop.prevent      → 阻止冒泡 + 阻止默認(rèn)行為
@scroll.passive          → 高性能滾動(dòng)監(jiān)聽(不能與 .prevent 同用)
@keydown.ctrl.exact.s    → 精確的 Ctrl+S(有其他鍵不觸發(fā))
@click.once              → 按鈕防重復(fù)點(diǎn)擊(僅首次有效)
@touchmove.prevent       → 阻止移動(dòng)端頁(yè)面隨手勢(shì)滾動(dòng)

十四、在 Render Function 中綁定事件

import { h, withModifiers } from 'vue'

// withModifiers:在 render function 中應(yīng)用事件修飾符
h('button', {
  onClick: withModifiers(
    (event: MouseEvent) => { console.log('clicked') },
    ['stop', 'prevent']  // 等價(jià)于 @click.stop.prevent
  )
})

// 多事件監(jiān)聽
h('input', {
  onInput: handler1,
  onChange: handler2,
  'onUpdate:modelValue': (val) => (modelValue.value = val)
})

// 動(dòng)態(tài)事件名
const eventName = 'onClick'
h('div', { [eventName]: handler })

總結(jié)

Vue 的事件系統(tǒng)本質(zhì)上是對(duì)原生 DOM 事件的完整代理。所有瀏覽器原生事件均可通過 @eventName 直接綁定,編譯器負(fù)責(zé)將其轉(zhuǎn)換為 onEventName 形式的 prop,運(yùn)行時(shí)通過 addEventListener 實(shí)際注冊(cè)。

幾個(gè)關(guān)鍵認(rèn)知

  • mouseenter/mouseleave 優(yōu)于 mouseover/mouseout(不冒泡,性能更好)
  • focusin/focusout 是父元素感知子元素焦點(diǎn)的正確方式
  • 中文輸入必須處理 compositionstart/end,否則 @input 會(huì)在選字中間觸發(fā)
  • @dragover.prevent@drop 能觸發(fā)的前提條件
  • Pointer Events 是未來趨勢(shì),統(tǒng)一了鼠標(biāo)、觸控、筆的處理
  • @scroll.passive 是長(zhǎng)列表滾動(dòng)性能的關(guān)鍵修飾符

到此這篇關(guān)于Vue事件綁定完全指南之官方文檔未詳述的事件大全的文章就介紹到這了,更多相關(guān)Vue事件綁定完全指南內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

大同县| 扶风县| 奉贤区| 察雅县| 安阳县| 江陵县| 长沙市| 孝昌县| 红安县| 莒南县| 武义县| 乐平市| 华安县| 大连市| 宁城县| 同德县| 洛川县| 桃园县| 丹巴县| 海丰县| 北票市| 石河子市| 沾化县| 滁州市| 体育| 东兴市| 永吉县| 娱乐| 镇平县| 阿尔山市| 平凉市| 武隆县| 瑞昌市| 海伦市| 泸水县| 云霄县| 铜梁县| 曲周县| 瑞安市| 灵台县| 浪卡子县|