Vue 事件修飾符最佳實踐
Vue 事件修飾符詳解
事件修飾符是 Vue 中處理 DOM 事件細節(jié)的強大工具。下面我將通過一個交互式示例全面解析各種事件修飾符的用法和原理。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue 事件修飾符詳解</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #2c3e50;
background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
header {
text-align: center;
padding: 30px 0;
margin-bottom: 30px;
}
h1 {
font-size: 2.8rem;
margin-bottom: 15px;
color: #34495e;
}
.subtitle {
color: #7f8c8d;
font-size: 1.3rem;
max-width: 800px;
margin: 0 auto;
}
.card {
background: white;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
padding: 30px;
margin-bottom: 30px;
}
.card-header {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px solid #f0f4f8;
display: flex;
align-items: center;
gap: 15px;
}
.card-header h2 {
font-size: 1.8rem;
color: #2c3e50;
}
.modifier-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 25px;
margin: 30px 0;
}
.modifier-card {
background: #f8fafc;
border-radius: 10px;
padding: 25px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
transition: transform 0.3s, box-shadow 0.3s;
}
.modifier-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
}
.modifier-card h3 {
color: #2c3e50;
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 10px;
}
.modifier-card h3 .icon {
background: #42b983;
color: white;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
}
.code-block {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 8px;
font-family: 'Fira Code', monospace;
font-size: 14px;
margin: 15px 0;
overflow-x: auto;
}
.demo-area {
background: white;
padding: 25px;
border-radius: 10px;
margin-top: 20px;
border: 1px solid #e2e8f0;
}
.demo-container {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.demo-box {
flex: 1;
min-width: 300px;
padding: 20px;
border-radius: 8px;
background: #f8fafc;
}
.event-target {
padding: 30px;
background: #e3f2fd;
border-radius: 8px;
margin: 15px 0;
position: relative;
cursor: pointer;
}
.event-target .inner {
padding: 20px;
background: #bbdefb;
border-radius: 6px;
}
.event-target .inner .core {
padding: 15px;
background: #90caf9;
border-radius: 4px;
}
.form-demo {
background: #e8f5e9;
padding: 20px;
border-radius: 8px;
margin-top: 20px;
}
button {
background: #42b983;
color: white;
border: none;
padding: 12px 20px;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
font-weight: 500;
transition: background 0.3s;
display: inline-block;
margin: 5px;
}
button:hover {
background: #3aa776;
}
.log-area {
background: #1e1e1e;
color: #d4d4d4;
padding: 20px;
border-radius: 8px;
margin-top: 20px;
font-family: 'Fira Code', monospace;
max-height: 300px;
overflow-y: auto;
font-size: 14px;
}
.log-entry {
margin-bottom: 8px;
padding-bottom: 8px;
border-bottom: 1px solid #333;
}
.log-time {
color: #6a9955;
}
.keyboard-demo {
display: flex;
gap: 15px;
flex-wrap: wrap;
margin: 20px 0;
}
.key {
background: #3498db;
color: white;
padding: 12px 18px;
border-radius: 6px;
cursor: pointer;
text-align: center;
min-width: 80px;
}
.mouse-buttons {
display: flex;
gap: 15px;
margin: 20px 0;
}
.mouse-btn {
background: #9b59b6;
color: white;
padding: 15px;
border-radius: 8px;
cursor: pointer;
text-align: center;
flex: 1;
}
.summary-table {
width: 100%;
border-collapse: collapse;
margin: 25px 0;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
}
.summary-table th, .summary-table td {
border: 1px solid #e2e8f0;
padding: 16px;
text-align: left;
}
.summary-table th {
background-color: #f8fafc;
font-weight: 600;
color: #2c3e50;
}
.summary-table tr:nth-child(even) {
background-color: #f8fafc;
}
.highlight {
background-color: #fff9c4;
padding: 2px 4px;
border-radius: 3px;
color: #333;
}
@media (max-width: 768px) {
.demo-container {
flex-direction: column;
}
h1 {
font-size: 2.2rem;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>Vue 事件修飾符詳解</h1>
<p class="subtitle">全面解析 Vue 事件修飾符的原理、用法及最佳實踐</p>
</header>
<div class="card">
<div class="card-header">
<h2>事件修飾符基礎(chǔ)</h2>
</div>
<p>事件修飾符是 Vue 為 v-on 指令提供的特殊后綴,用于處理 DOM 事件細節(jié):</p>
<div class="modifier-grid">
<!-- .stop 修飾符 -->
<div class="modifier-card">
<h3><span class="icon">?</span> .stop</h3>
<p>阻止事件冒泡(相當(dāng)于 event.stopPropagation())</p>
<div class="code-block">
<div @click.stop="handleClick">...</div>
</div>
<p class="demo-hint">點擊內(nèi)部元素不會觸發(fā)外部元素的事件</p>
</div>
<!-- .prevent 修飾符 -->
<div class="modifier-card">
<h3><span class="icon">??</span> .prevent</h3>
<p>阻止默認行為(相當(dāng)于 event.preventDefault())</p>
<div class="code-block">
<form @submit.prevent="onSubmit">...</form>
</div>
<p class="demo-hint">阻止表單提交、鏈接跳轉(zhuǎn)等默認行為</p>
</div>
<!-- .capture 修飾符 -->
<div class="modifier-card">
<h3><span class="icon">??</span> .capture</h3>
<p>使用事件捕獲模式(從外到內(nèi)處理事件)</p>
<div class="code-block">
<div @click.capture="handleCapture">...</div>
</div>
<p class="demo-hint">先處理外部元素,再處理內(nèi)部元素</p>
</div>
<!-- .self 修飾符 -->
<div class="modifier-card">
<h3><span class="icon">??</span> .self</h3>
<p>只當(dāng)事件是從元素自身觸發(fā)時觸發(fā)</p>
<div class="code-block">
<div @click.self="handleSelf">...</div>
</div>
<p class="demo-hint">忽略內(nèi)部元素冒泡上來的事件</p>
</div>
<!-- .once 修飾符 -->
<div class="modifier-card">
<h3><span class="icon">1??</span> .once</h3>
<p>事件只觸發(fā)一次</p>
<div class="code-block">
<button @click.once="handleOnce">...</button>
</div>
<p class="demo-hint">第一次點擊后自動移除事件監(jiān)聽</p>
</div>
<!-- .passive 修飾符 -->
<div class="modifier-card">
<h3><span class="icon">?</span> .passive</h3>
<p>提升滾動性能(不阻止默認行為)</p>
<div class="code-block">
<div @scroll.passive="onScroll">...</div>
</div>
<p class="demo-hint">特別針對移動端滾動優(yōu)化</p>
</div>
</div>
<div class="demo-area">
<h3>事件修飾符演示</h3>
<div class="demo-container">
<div class="demo-box">
<h4>事件冒泡測試</h4>
<div class="event-target" @click="logEvent('outer')">
外層區(qū)域
<div class="inner" @click="logEvent('inner')">
中間區(qū)域
<div class="core" @click="logEvent('core')">
核心區(qū)域
</div>
</div>
</div>
<div class="keyboard-demo">
<button @click="clearLogs">清除日志</button>
<button @click="addStopModifier('core')">核心添加.stop</button>
<button @click="addSelfModifier('inner')">中間添加.self</button>
</div>
</div>
<div class="demo-box">
<h4>默認行為測試</h4>
<div class="form-demo">
<form @submit="logEvent('表單提交')">
<p>
<input type="text" placeholder="輸入內(nèi)容"
style="width:100%; padding:10px; margin:10px 0;">
</p>
<button type="submit">普通提交</button>
<button type="submit" @click.prevent="logEvent('阻止提交')">阻止提交</button>
<button type="submit" @click.prevent.once="logEvent('只提交一次')">只提交一次</button>
</form>
<div style="margin-top: 20px;">
<a rel="external nofollow" rel="external nofollow" @click="logEvent('普通鏈接')">普通鏈接</a>
<a rel="external nofollow" rel="external nofollow" @click.prevent="logEvent('阻止跳轉(zhuǎn)')">阻止跳轉(zhuǎn)鏈接</a>
</div>
</div>
</div>
</div>
<div class="log-area">
<div v-for="(log, index) in eventLog" :key="index" class="log-entry">
<span class="log-time">{{ log.time }}</span> - {{ log.message }}
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h2>按鍵修飾符</h2>
</div>
<p>Vue 提供按鍵修飾符來處理鍵盤事件:</p>
<div class="demo-area">
<div class="keyboard-demo">
<div class="key" @keydown.enter="handleKey('Enter')">Enter</div>
<div class="key" @keydown.esc="handleKey('Esc')">Esc</div>
<div class="key" @keydown.space="handleKey('Space')">Space</div>
<div class="key" @keydown.up="handleKey('↑')">↑</div>
<div class="key" @keydown.down="handleKey('↓')">↓</div>
<div class="key" @keydown.left="handleKey('←')">←</div>
<div class="key" @keydown.right="handleKey('→')">→</div>
<div class="key" @keydown.delete="handleKey('Delete')">Delete</div>
<div class="key" @keydown.tab="handleKey('Tab')">Tab</div>
</div>
<div class="keyboard-demo">
<div class="key" @keydown.ctrl.exact="handleKey('Ctrl')">Ctrl</div>
<div class="key" @keydown.alt.exact="handleKey('Alt')">Alt</div>
<div class="key" @keydown.shift.exact="handleKey('Shift')">Shift</div>
<div class="key" @keydown.meta.exact="handleKey('Meta')">Meta</div>
</div>
<div class="keyboard-demo">
<div class="key" @keydown.ctrl.enter="handleKey('Ctrl+Enter')">Ctrl+Enter</div>
<div class="key" @keydown.alt.space="handleKey('Alt+Space')">Alt+Space</div>
<div class="key" @keydown.shift.up="handleKey('Shift+↑')">Shift+↑</div>
</div>
<input type="text" placeholder="在此輸入內(nèi)容測試按鍵事件"
@keydown="logKeyEvent"
style="width:100%; padding:12px; border-radius:6px; border:1px solid #ddd; margin:15px 0;">
<div class="log-area">
<div v-for="(log, index) in keyLog" :key="index" class="log-entry">
<span class="log-time">{{ log.time }}</span> - {{ log.message }}
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h2>鼠標(biāo)修飾符</h2>
</div>
<div class="demo-area">
<div class="mouse-buttons">
<div class="mouse-btn" @click="handleMouse('左鍵')">左鍵點擊</div>
<div class="mouse-btn" @click.right="handleMouse('右鍵')">右鍵點擊</div>
<div class="mouse-btn" @click.middle="handleMouse('中鍵')">中鍵點擊</div>
<div class="mouse-btn" @click.left="handleMouse('左鍵')">左鍵點擊</div>
</div>
<div class="mouse-buttons">
<div class="mouse-btn" @click.ctrl="handleMouse('Ctrl+點擊')">Ctrl+點擊</div>
<div class="mouse-btn" @click.shift="handleMouse('Shift+點擊')">Shift+點擊</div>
<div class="mouse-btn" @click.alt="handleMouse('Alt+點擊')">Alt+點擊</div>
<div class="mouse-btn" @click.exact="handleMouse('精確點擊')">精確點擊</div>
</div>
<div class="log-area">
<div v-for="(log, index) in mouseLog" :key="index" class="log-entry">
<span class="log-time">{{ log.time }}</span> - {{ log.message }}
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h2>事件修飾符總結(jié)</h2>
</div>
<div class="summary-table">
<table>
<thead>
<tr>
<th>修飾符</th>
<th>作用</th>
<th>原生 JS 等效操作</th>
<th>使用場景</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>.stop</code></td>
<td>阻止事件冒泡</td>
<td><code>event.stopPropagation()</code></td>
<td>阻止內(nèi)部事件觸發(fā)外部事件</td>
</tr>
<tr>
<td><code>.prevent</code></td>
<td>阻止默認行為</td>
<td><code>event.preventDefault()</code></td>
<td>阻止表單提交、鏈接跳轉(zhuǎn)</td>
</tr>
<tr>
<td><code>.capture</code></td>
<td>使用捕獲模式</td>
<td>在事件捕獲階段處理</td>
<td>需要先處理父級再處理子級</td>
</tr>
<tr>
<td><code>.self</code></td>
<td>僅自身觸發(fā)時處理</td>
<td>檢查 <code>event.target</code></td>
<td>忽略子元素冒泡事件</td>
</tr>
<tr>
<td><code>.once</code></td>
<td>只觸發(fā)一次</td>
<td>觸發(fā)后移除事件監(jiān)聽</td>
<td>一次性操作(如首次點擊)</td>
</tr>
<tr>
<td><code>.passive</code></td>
<td>提升滾動性能</td>
<td><code>{ passive: true }</code></td>
<td>移動端滾動優(yōu)化</td>
</tr>
<tr>
<td><code>.{keyCode}</code></td>
<td>按鍵事件</td>
<td><code>event.keyCode</code></td>
<td>處理特定按鍵事件</td>
</tr>
<tr>
<td><code>.native</code></td>
<td>監(jiān)聽組件根元素</td>
<td>原生事件綁定</td>
<td>在組件上監(jiān)聽原生事件</td>
</tr>
</tbody>
</table>
</div>
<div class="demo-area">
<h3>最佳實踐</h3>
<ul>
<li>使用修飾符代替直接在方法中操作 DOM 事件</li>
<li>注意修飾符的順序:<code>@click.prevent.self</code> 與 <code>@click.self.prevent</code> 不同</li>
<li>移動端優(yōu)先使用 <code>.passive</code> 提升滾動性能</li>
<li>使用 <code>.exact</code> 修飾符精確控制系統(tǒng)修飾符組合</li>
<li>自定義按鍵別名:<code>Vue.config.keyCodes.f1 = 112</code></li>
</ul>
<div class="code-block">
// 修飾符串聯(lián)示例
<a @click.stop.prevent="handleLink">鏈接</a>
// 精確控制系統(tǒng)修飾符
<button @click.ctrl.exact="ctrlClick">Ctrl+點擊</button>
// 自定義按鍵別名
Vue.config.keyCodes = {
f1: 112,
mediaPlayPause: 179
}
</div>
</div>
</div>
</div>
<script>
new Vue({
el: '.container',
data: {
eventLog: [],
keyLog: [],
mouseLog: [],
stopModifier: '',
selfModifier: ''
},
methods: {
logEvent(source) {
const time = new Date().toLocaleTimeString();
this.eventLog.unshift({
time,
message: `事件來源: ${source}`
});
if (this.eventLog.length > 20) {
this.eventLog.pop();
}
},
logKeyEvent(event) {
const time = new Date().toLocaleTimeString();
this.keyLog.unshift({
time,
message: `按鍵: ${event.key} (代碼: ${event.code})`
});
if (this.keyLog.length > 15) {
this.keyLog.pop();
}
},
handleKey(key) {
const time = new Date().toLocaleTimeString();
this.keyLog.unshift({
time,
message: `檢測到按鍵: ${key}`
});
},
handleMouse(button) {
const time = new Date().toLocaleTimeString();
this.mouseLog.unshift({
time,
message: `鼠標(biāo)操作: ${button}`
});
},
clearLogs() {
this.eventLog = [];
this.keyLog = [];
this.mouseLog = [];
},
addStopModifier(element) {
this.stopModifier = element;
this.logEvent(`為${element}元素添加了.stop修飾符`);
},
addSelfModifier(element) {
this.selfModifier = element;
this.logEvent(`為${element}元素添加了.self修飾符`);
},
// 模擬修飾符效果
handleCoreClick(event) {
if (this.stopModifier === 'core') {
event.stopPropagation();
}
this.logEvent('核心區(qū)域');
},
handleInnerClick(event) {
if (this.selfModifier === 'inner') {
if (event.target !== event.currentTarget) return;
}
this.logEvent('中間區(qū)域');
}
}
});
</script>
</body>
</html>Vue 事件修飾符詳解
核心事件修飾符
.stop- 阻止事件冒泡- 等效于
event.stopPropagation()停止事件冒泡 - 使用場景:阻止內(nèi)部事件觸發(fā)外部事件
- 等效于
.prevent- 阻止默認行為- 等效于
event.preventDefault()阻止事件的默認行為。 - 使用場景:阻止表單提交、鏈接跳轉(zhuǎn)等
- 等效于
.capture- 使用事件捕獲模式- 事件從外到內(nèi)處理(默認是冒泡模式,從內(nèi)到外)
- 添加事件監(jiān)聽器包括兩種不同的方式:
- 一種是從內(nèi)到外添加(是事件冒泡模式)
- 一種是從外到內(nèi)添加(是事件捕獲模式)
- 添加事件監(jiān)聽器包括兩種不同的方式:
- 使用場景:需要先處理父級再處理子級的事件
- 事件從外到內(nèi)處理(默認是冒泡模式,從內(nèi)到外)
.self- 僅當(dāng)事件源是元素自身時觸發(fā)- 忽略子元素冒泡上來的事件
- 使用場景:只在點擊元素自身時觸發(fā),忽略內(nèi)部元素事件
.once- 事件只觸發(fā)一次- 觸發(fā)后自動移除事件監(jiān)聽
- 使用場景:一次性操作(如首次點擊)
.passive- 提升滾動性能(passive意為順從、不抵抗。直接繼續(xù)(立即)執(zhí)行事件的默認行為)- 告訴瀏覽器不阻止默認行為
- 使用場景:移動端滾動優(yōu)化
- 注意:.passive和.prevent修飾符是對立的。兩者不可以共存(如果一起用,就會報錯)。
- .prevent :阻止事件的默認行為
- .passive:解除阻止事件的默認行為
注意:在Vue當(dāng)中,使勁按修飾符可以多個聯(lián)合使用,例如:
@click.self.stop:先執(zhí)行.self.再執(zhí)行.stop
按鍵修飾符
Vue 提供了常見按鍵的別名:
.enter.tab.delete(捕獲 “刪除” 和 “退格” 鍵).esc.space.up.down.left.right
系統(tǒng)修飾鍵:
.ctrl.alt.shift.meta(Windows 鍵或 Command 鍵)
鼠標(biāo)修飾符
.left- 左鍵點擊.right- 右鍵點擊.middle- 中鍵點擊
修飾符使用技巧
修飾符串聯(lián):
<!-- 先停止冒泡,再阻止默認行為 --> <a @click.stop.prevent="doThat"></a>
精確修飾符:
<!-- 有且只有 Ctrl 被按下時才觸發(fā) --> <button @click.ctrl.exact="onCtrlClick">Ctrl+Click</button> <!-- 沒有任何系統(tǒng)修飾符被按下時才觸發(fā) --> <button @click.exact="onClick">Click</button>
自定義按鍵別名:
// 全局配置
Vue.config.keyCodes = {
f1: 112,
mediaPlayPause: 179
}
// 使用
<input @keyup.f1="help" @keyup.mediaPlayPause="playPause">最佳實踐
- 使用修飾符替代在方法中操作 DOM 事件
- 注意修飾符的順序(從左到右處理)
- 移動端優(yōu)先使用
.passive提升滾動性能 - 使用
.exact精確控制系統(tǒng)修飾鍵組合 - 避免過度使用
.once,確保用戶理解操作是一次性的
到此這篇關(guān)于Vue 事件修飾符最佳實踐的文章就介紹到這了,更多相關(guān)Vue 事件修飾符內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue監(jiān)聽鍵盤事件的相關(guān)總結(jié)
這篇文章主要介紹了vue監(jiān)聽鍵盤事件的相關(guān)總結(jié),幫助大家更好的理解和使用vue框架,感興趣的朋友可以了解下2021-01-01
Vue3使用Vuex之mapState與mapGetters詳解
這篇文章主要為大家介紹了Vue3使用Vuex之mapState與mapGetters詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03
Vue.js 的移動端組件庫mint-ui實現(xiàn)無限滾動加載更多的方法
下面小編就為大家分享一篇Vue.js 的移動端組件庫mint-ui實現(xiàn)無限滾動加載更多的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12
vue中的$emit 與$on父子組件與兄弟組件的之間通信方式
本文主要對vue 用$emit 與 $on 來進行組件之間的數(shù)據(jù)傳輸。重點給大家介紹vue中的$emit 與$on父子組件與兄弟組件的之間通信方式,感興趣的朋友一起看看2018-05-05
vue的路由守衛(wèi)和keep-alive后生命周期詳解
這篇文章主要為大家詳細介紹了vue路由守衛(wèi)和keep-alive,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03
去除Element-Plus下拉菜單邊框的實現(xiàn)步驟
Element-Plus 是 Element UI 的 Vue 3 版本,它提供了一套完整的組件庫,在使用 Element-Plus 進行開發(fā)時,我們可能會遇到需要自定義組件樣式的情況,本文將介紹如何使用 CSS 來去除 Element-Plus 下拉框的邊框,需要的朋友可以參考下2024-03-03

