VUE事件修饰符

简介

在原生DOM绑定事件的年代,我们经常会使用到e.preventDefault() 或 e.stopPropagation()等操作。

document.getElementById('menu').onclick = function(e) {
  e.preventDefault();
  //...
}

Vue.js 为了简化这种常见需求,为v-on提供了一个叫Event Modifiers (事件修饰符)的语法糖。vue2共提供了五个事件修饰符:

  • .stop 阻止事件向上冒泡,等价于添加event.stopPropagation()
  • .prevent 阻止元素发生默认的行为,等价于添加event.preventDefault()
  • .capture 在捕获阶段触发监听函数
  • .self 只当 event.target === event.currentTarget 时触发处理函数
  • .once 事件将只会触发一次
  • .passive 表示 listener 永远不会调用 preventDefault()

事件传播

W3C的DOM标准中,一次事件包含三个步骤:

捕获 -> 到达目标 -> 冒泡

举个简单的例子,当点击Inner标签时,事件传播顺序是 Outer -> Middle -> Inner -> Inner -> Middle -> Outer

outer-middle-inner

<main @click.capture="listener($event, 'Capture')">
    Outer
    <section v-on:click="listener($event, 'Bubble')">
      Middle
      <a href="javascript:console.log('default')"> Inner </a>
    </section>
</main>
  1. 事件捕获阶段(event capturing)
    通俗来说就是当点击Inner标签后,浏览器会从根节点由外到内进行事件传播。
    Event Capturing
  1. 事件冒泡阶段 (event bubbling)
    捕获阶段结束后,事件到达目标元素,接着就开始从内往外传播事件。


    Event Bubbling

要阻止冒泡继续向外传播的话,添加.stop修饰符即可停止冒泡。如下,再给<section @click.stop="listener">.stop后,只会打印SECTION Bubble

<template>
  <main class="hello"  @click="listener">
      Outer
      <section @click.stop="listener">
        Middle
        <a href="javascript:void(0)"> Inner </a>
      </section>
  </main>
</template>

<script>
export default {
  name: 'HelloWorld',
  methods: {
    listener: function (e, msg = 'Bubble') {
      const current = e.currentTarget.nodeName
      console.log(`${current} ${msg}`)
    },
  },
}
</script>

事件监听

v-on监听DOM事件,本质上是调用了addEventListener

/**
 * @param type 事件类型,如click
 * @param listener 监听函数
 * @param useCapture 是否采用事件捕获
 */
addEventListener(type, listener, useCapture=false)

默认的v-on:click="listener" (@clickv-on:click语法糖), 等价于addEventListener('click', listener)。所以一般的v-on监听只在冒泡阶段才触发。若想在捕获阶段触发事件必须加上.capture修饰符。

注意:v-on:click.capture只在捕获阶段触发,不会在冒泡阶段触发。要想两个阶段都触发,只能写两套:

<div @click.capture="listener" @click="listener" >
...
</div>

事件委托

当页面中存在大量元素而且每一个都要一次或多次绑定事件处理器时,大量的事件绑定会影响页面性能。一个简单而有效的技术就是事件委托。它也是基于冒泡机制,只要给父级元素绑定处理器,就可代理子级元素上的所有事件。稍微修改一下原例子:

<main class="hello">
    Outer
    <section @click.prevent="listener">
      Middle
      <a href="#1">Inner1</a>
      <a href="#2">Inner2</a>
      <a href="#3">Inner3</a>
    </section>
</main>
listener: function (e) {
      console.log(`Get ${e.target.href}`)
    }

<section @click.prevent="listener">加上.prevent修饰符阻止<a>标签的默认跳转行为。当点击<a>元素时,事件向DOM树上层冒泡,被<section>元素接受,然后触发listener。这样,仅添加一个listener到父级元素,就可以实现代理了。

其他

上面提到了.stop.prevent.capture,再简单介绍一下剩余几个修饰符。addEventListener其实还有另一种调用方法:

/**
 * capture: 表示 listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发。
 * 
 * passive: 表示 listener 永远不会调用 preventDefault()。
 *          如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。
 *          
 * once:    表示 listener 在添加之后最多只调用一次。
 *          如果是 true,listener 会在其被调用之后自动移除。
 */
addEventListener(type, listener ,
       {capture: Boolean, passive: Boolean, once: Boolean});

.capture .once .passive本质上就是给上述三个Boolean值赋true。

.self是vue自带的语法糖,表示事件由本身触发,不是来自子节点。顺带提一下,使用修饰符时顺序很重要,在某些场景中:

v-on:click.prevent.self会阻止所有的点击,而v-on:click.self.prevent只会阻止对元素自身的点击。

小结

今天通过vue的事件修饰符,简单回顾了一下浏览器的事件机制。以react,angular,vue为代表的前端框架极大地提升了工程师的开发体验,但是学完框架不去了解冒泡、捕获、传播、委托等基础知识,当需要解决更细节的问题时就略显颟顸了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容