x-on 事件处理 #

什么是 x-on? #

x-on 指令用于监听 DOM 事件并执行相应的处理代码。它是实现用户交互的核心指令。

基本语法 #

完整语法:

html
<button x-on:事件名="处理代码"></button>

简写语法(推荐):

html
<button @事件名="处理代码"></button>

基本用法 #

点击事件 #

html
<div x-data="{ count: 0 }">
    <button @click="count++">点击次数: <span x-text="count"></span></button>
</div>

调用方法 #

html
<div x-data="{
    message: '',
    greet() {
        this.message = 'Hello!'
    }
}">
    <button @click="greet()">问候</button>
    <p x-text="message"></p>
</div>

传递参数 #

html
<div x-data="{
    result: 0,
    add(a, b) {
        this.result = a + b
    }
}">
    <button @click="add(5, 3)">计算 5 + 3</button>
    <p>结果: <span x-text="result"></span></p>
</div>

访问事件对象 #

html
<div x-data>
    <button @click="alert($event.target.textContent)">
        点击我
    </button>
</div>

内联处理 #

html
<div x-data="{ show: false }">
    <button @click="show = !show">切换</button>
    <div x-show="show">内容</div>
</div>

常用事件 #

鼠标事件 #

html
<div x-data="{
    handleClick() { console.log('点击') },
    handleDblClick() { console.log('双击') },
    handleMouseDown() { console.log('鼠标按下') },
    handleMouseUp() { console.log('鼠标释放') },
    handleMouseEnter() { console.log('鼠标进入') },
    handleMouseLeave() { console.log('鼠标离开') },
    handleMouseMove() { console.log('鼠标移动') }
}">
    <div 
        @click="handleClick()"
        @dblclick="handleDblClick()"
        @mousedown="handleMouseDown()"
        @mouseup="handleMouseUp()"
        @mouseenter="handleMouseEnter()"
        @mouseleave="handleMouseLeave()"
        @mousemove="handleMouseMove()"
        style="padding: 50px; background: #f0f0f0;"
    >
        鼠标事件区域
    </div>
</div>

键盘事件 #

html
<div x-data="{
    handleKeydown(e) { console.log('按下:', e.key) },
    handleKeyup(e) { console.log('释放:', e.key) }
}">
    <input 
        @keydown="handleKeydown($event)"
        @keyup="handleKeyup($event)"
        placeholder="按下键盘"
    >
</div>

表单事件 #

html
<div x-data="{
    value: '',
    handleSubmit() { console.log('提交:', this.value) },
    handleInput(e) { console.log('输入:', e.target.value) },
    handleChange(e) { console.log('改变:', e.target.value) },
    handleFocus() { console.log('聚焦') },
    handleBlur() { console.log('失焦') }
}">
    <form @submit.prevent="handleSubmit()">
        <input 
            x-model="value"
            @input="handleInput($event)"
            @change="handleChange($event)"
            @focus="handleFocus()"
            @blur="handleBlur()"
        >
        <button type="submit">提交</button>
    </form>
</div>

事件修饰符 #

.prevent #

阻止默认行为(相当于 event.preventDefault()):

html
<form @submit.prevent="handleSubmit()">
    <button type="submit">提交</button>
</form>

<a href="https://example.com" @click.prevent="handleClick()">
    链接
</a>

.stop #

阻止事件冒泡(相当于 event.stopPropagation()):

html
<div @click="handleOuter()">
    <button @click.stop="handleInner()">
        点击不会触发外层事件
    </button>
</div>

.once #

只触发一次:

html
<button @click.once="handleClick()">
    只能点击一次
</button>

.capture #

使用捕获模式:

html
<div @click.capture="handleCapture()">
    <button @click="handleBubble()">点击</button>
</div>

.self #

只在事件目标是当前元素时触发:

html
<div @click.self="handleClick()" style="padding: 20px; background: #f0f0f0;">
    <button>点击按钮不会触发 div 的事件</button>
</div>

.passive #

提升移动端滚动性能:

html
<div @scroll.passive="handleScroll()">
    滚动内容
</div>

.away #

点击元素外部时触发:

html
<div x-data="{ open: false }">
    <button @click="open = true">打开</button>
    <div 
        x-show="open" 
        @click.away="open = false"
        style="padding: 20px; background: #f0f0f0;"
    >
        点击外部关闭
    </div>
</div>

.debounce #

防抖处理:

html
<input 
    @input.debounce="search()"
    placeholder="搜索..."
>

指定防抖时间:

html
<input 
    @input.debounce.500ms="search()"
    placeholder="搜索..."
>

.throttle #

节流处理:

html
<button @click.throttle="handleClick()">
    限制点击频率
</button>

指定节流时间:

html
<button @click.throttle.1000ms="handleClick()">
    每秒最多触发一次
</button>

按键修饰符 #

常用按键 #

html
<input @keyup.enter="submit()">
<input @keyup.tab="next()">
<input @keyup.escape="cancel()">
<input @keyup.space="toggle()">
<input @keyup.up="moveUp()">
<input @keyup.down="moveDown()">
<input @keyup.left="moveLeft()">
<input @keyup.right="moveRight()">

组合键 #

html
<div @keydown.ctrl.s="save()"></div>
<div @keydown.ctrl.enter="submit()"></div>
<div @keydown.shift.enter="newLine()"></div>
<div @keydown.alt.s="saveAs()"></div>
<div @keydown.meta.s="save()"></div>

自定义按键码 #

html
<input @keyup.13="submit()">

鼠标修饰符 #

按键组合 #

html
<div @click.left="handleLeftClick()">左键点击</div>
<div @click.right="handleRightClick()">右键点击</div>
<div @click.middle="handleMiddleClick()">中键点击</div>

组合修饰符 #

html
<div @click.ctrl="handleCtrlClick()">Ctrl + 点击</div>
<div @click.shift="handleShiftClick()">Shift + 点击</div>
<div @click.alt="handleAltClick()">Alt + 点击</div>
<div @click.meta="handleMetaClick()">Meta + 点击</div>

实用示例 #

下拉菜单 #

html
<div x-data="{ open: false }" class="dropdown">
    <button @click="open = !open">
        菜单
    </button>
    <div 
        x-show="open" 
        @click.away="open = false"
        class="dropdown-menu"
    >
        <a href="#" @click.prevent="open = false">选项 1</a>
        <a href="#" @click.prevent="open = false">选项 2</a>
        <a href="#" @click.prevent="open = false">选项 3</a>
    </div>
</div>

模态框 #

html
<div x-data="{ open: false }">
    <button @click="open = true">打开模态框</button>
    
    <div 
        x-show="open" 
        x-transition
        @keydown.escape.window="open = false"
        class="modal-overlay"
        @click.self="open = false"
    >
        <div class="modal-content" @click.stop>
            <h2>模态框标题</h2>
            <p>内容</p>
            <button @click="open = false">关闭</button>
        </div>
    </div>
</div>

拖拽功能 #

html
<div x-data="{
    dragging: false,
    x: 0,
    y: 0,
    startX: 0,
    startY: 0,
    startDrag(e) {
        this.dragging = true
        this.startX = e.clientX - this.x
        this.startY = e.clientY - this.y
    },
    onDrag(e) {
        if (!this.dragging) return
        this.x = e.clientX - this.startX
        this.y = e.clientY - this.startY
    },
    endDrag() {
        this.dragging = false
    }
}" @mousemove.window="onDrag($event)" @mouseup.window="endDrag()">
    <div 
        @mousedown="startDrag($event)"
        :style="`transform: translate(${x}px, ${y}px)`"
        class="draggable"
    >
        拖拽我
    </div>
</div>

快捷键 #

html
<div x-data="{
    showHelp: false,
    save() { console.log('保存') },
    undo() { console.log('撤销') },
    redo() { console.log('重做') },
    copy() { console.log('复制') },
    paste() { console.log('粘贴') }
}" @keydown.window.ctrl.s.prevent="save()" @keydown.window.ctrl.z.prevent="undo()" @keydown.window.ctrl.y.prevent="redo()" @keydown.window.ctrl.c.prevent="copy()" @keydown.window.ctrl.v.prevent="paste()" @keydown.window.slash="showHelp = !showHelp">
    <p>快捷键:</p>
    <ul>
        <li>Ctrl+S: 保存</li>
        <li>Ctrl+Z: 撤销</li>
        <li>Ctrl+Y: 重做</li>
        <li>/: 帮助</li>
    </ul>
    
    <div x-show="showHelp" @click.away="showHelp = false">
        帮助信息
    </div>
</div>

搜索输入 #

html
<div x-data="{
    query: '',
    results: [],
    async search() {
        if (this.query.length < 2) {
            this.results = []
            return
        }
        const res = await fetch(`/api/search?q=${this.query}`)
        this.results = await res.json()
    }
}">
    <input 
        x-model="query"
        @input.debounce.300ms="search()"
        @keydown.escape="results = []"
        @keydown.up="selectPrevious()"
        @keydown.down="selectNext()"
        @keydown.enter="selectCurrent()"
        placeholder="搜索..."
    >
    
    <ul x-show="results.length > 0">
        <template x-for="result in results" :key="result.id">
            <li x-text="result.name"></li>
        </template>
    </ul>
</div>

右键菜单 #

html
<div x-data="{
    show: false,
    x: 0,
    y: 0,
    openMenu(e) {
        e.preventDefault()
        this.show = true
        this.x = e.clientX
        this.y = e.clientY
    }
}" @contextmenu="openMenu($event)" @click.away="show = false">
    右键点击这里
    
    <div 
        x-show="show"
        x-transition
        :style="`position: fixed; left: ${x}px; top: ${y}px`"
        class="context-menu"
    >
        <div @click="show = false">选项 1</div>
        <div @click="show = false">选项 2</div>
        <div @click="show = false">选项 3</div>
    </div>
</div>

复制到剪贴板 #

html
<div x-data="{
    text: 'Hello World',
    copied: false,
    async copy() {
        await navigator.clipboard.writeText(this.text)
        this.copied = true
        setTimeout(() => this.copied = false, 2000)
    }
}">
    <input :value="text" readonly>
    <button @click="copy()">
        <span x-show="!copied">复制</span>
        <span x-show="copied">已复制!</span>
    </button>
</div>

注意事项 #

1. 方法调用 #

需要显式调用方法:

html
<button @click="handleClick()">正确</button>
<button @click="handleClick">错误(不会执行)</button>

2. 事件对象 #

内联表达式中可直接使用 $event

html
<button @click="console.log($event)">点击</button>

3. 修饰符顺序 #

修饰符按顺序执行:

html
<button @click.stop.prevent="handler()">
    先 stop 再 prevent
</button>

小结 #

x-on 指令要点:

  • 使用 @ 简写语法监听事件
  • 支持各种 DOM 事件
  • 提供丰富的事件修饰符
  • 支持按键和鼠标修饰符
  • 支持防抖和节流

下一章,我们将学习 x-init 指令进行初始化。

最后更新:2026-03-28