Vue 事件的高级使用方法

事件方法

在Vue中提供了4中事件监听方法,分别是:

  • $on(event: string | Array, fn)
  • $emit(event: string)
  • $once(event: string, fn)
  • $off(event?: string|Array, fn?)

1. $on

监听当前实例上的自定义事件。事件可以由 vm.$emit 触发。回调函数会接收所有传入事件触发函数的额外参数。

从上述传参可以看出。第一个参数可以传递一个字符串,或者一个数组,如果传递的是数组,则会对数组中的每一个事件进行监听,只要有一个触发,就会执行后面的回调函数

mounted () {
this.$on('event1', () => {
console.log('ok');
}); this.$on(['event1', 'event2', 'event3'], () => {
console.log('ok');
}); setTimeout(() => {
this.$emit('event1');
}, 1000);
}

当调用 $emit 之后,两个 $on 都会被触发

2. $emit

触发当前实例上的事件。附加参数都会传给监听器回调。

此处的用法非常简单,可以和 $on 配合使用,也可以和组件中的自定义事件配合使用

Vue.component('welcome-button', {
template: `
<button v-on:click="$emit('welcome')">
Click me to be welcomed
</button>
`
}); <div id="emit-example-simple">
<welcome-button v-on:welcome="sayHi"></welcome-button>
</div> methods: {
sayHi: function () {
alert('Hi!')
}
}

3. $once

监听一个自定义事件,但是只触发一次。一旦触发之后,监听器就会被移除。

4. $off

移除自定义事件监听器

  • 如果没有提供参数,则移除所有的事件监听器;
  • 如果只提供了事件,则移除该事件所有的监听器;
  • 如果同时提供了事件与回调,则只移除这个回调的监听器。

高级用法

1. 使用$on $emit 实现跨组件通信

有时两个组件隔的比较远的情况下,可以通过这两个组件的共同父元素,或者跟节点来派发和监听事件,达到通信的效果。

// components A
<div>
<button @click="clickHandler">click me</button>
</div> export default {
name: 'componentsA',
method: {
clickHandler() {
this.$root.$emit('my-events');
}
}
} // components B
export default {
name: 'componentsB',
mounted () {
this.$root.$on('my-events', () => {
console.log('ok');
});
}
}
// 这里的$root可以换成共同的父元素

2. hookEvents

父组件可以直接通过自定义事件的形式监听子组件中声明周期钩子的变化,固定写法 <componentsA @hook:update="func" />, 目的是当使用了第三方组件,还想要知道里面的生命周期触发了。可以使用此方法监听

// child Events
export default {
name: 'Events2',
data () {
return {
counter: 0
};
},
mounted () {
setTimeout(() => {
this.counter += 1;
}, 2000);
},
updated () {
console.log('update');
}
}; // parent components
<Event2 @hook:updated="updateCounter"/> export default {
components: {
Events2
},
methods: {
updateCounter () {
console.log('hooks ok');
}
}
}

源码分析

  1. 定义事件源码位置:vue/src/core/instance/events.js
export function eventsMixin (Vue: Class<Component>) {
const hookRE = /^hook:/ // hookEvent
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component { // 除了可以使用字符串,还可以监听一个数组,['event1', 'event2']
const vm: Component = this
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn)
}
} else {
// 把事件名称和回调函数存入vm._events中
(vm._events[event] || (vm._events[event] = [])).push(fn) // 一个事件可以对应多个回调函数
// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash lookup
if (hookRE.test(event)) { // 监听生命周期的钩子事件
vm._hasHookEvent = true // 只要有人监听这个事件。在callHooks中就是会派发一个事件
}
}
return vm
} Vue.prototype.$once = function (event: string, fn: Function): Component {
const vm: Component = this
function on () {
vm.$off(event, on) // 执行一次回调函数后就立刻结束
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
} Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
const vm: Component = this
// all
if (!arguments.length) { // 无参数,清除所有的事件监听
vm._events = Object.create(null)
return vm
}
// array of events
if (Array.isArray(event)) { // 传入的数组,把相关的事件移除
for (let i = 0, l = event.length; i < l; i++) {
vm.$off(event[i], fn)
}
return vm
}
// specific event 解除特定事件
const cbs = vm._events[event]
if (!cbs) {
return vm
}
if (!fn) { // 如果用户没指定fn参数,相关的所有回调都清除
vm._events[event] = null
return vm
}
// specific handler // 如果用户指定fn参数,只移除对应的回调
let cb
let i = cbs.length
while (i--) {
cb = cbs[i]
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break
}
}
return vm
} Vue.prototype.$emit = function (event: string): Component { // 事件派发
const vm: Component = this
let cbs = vm._events[event]
if (cbs) {
cbs = cbs.length > 1 ? toArray(cbs) : cbs
const args = toArray(arguments, 1)
const info = `event handler for "${event}"`
for (let i = 0, l = cbs.length; i < l; i++) {
invokeWithErrorHandling(cbs[i], vm, args, vm, info)
}
}
return vm
}
}
  1. 派发hookEvents位置:vue/src/core/instance/lifecycle.js
export function callHook (vm: Component, hook: string) {
if (vm._hasHookEvent) { // 如果标记了钩子事件,则额外的派发一个自定义的事件出去
vm.$emit('hook:' + hook) // 比如:@hook:update="xxx"
}
}

测试代码地址:https://github.com/Shenjieping/vue-events

Vue 事件的高级使用方法的更多相关文章

  1. vue事件绑定处理

    事件监听指令 v-on 指令监听 DOM 事件来触发一些 JavaScript 代码,通常是触发一个函数,简写@ <template> <div id="app" ...

  2. Vue2.x源码学习笔记-Vue实例的属性和方法整理

    还是先从浏览器直观的感受下实例属性和方法. 实例属性: 对应解释如下: vm._uid // 自增的id vm._isVue // 标示是vue对象,避免被observe vm._renderProx ...

  3. vue解决遮罩层滚动方法

    vue 遮罩层阻止默认滚动事件 在写移动端页面的时候,弹出遮罩层后,我们仍然可以滚动页面. vue中提供 @touchmove.prevent 方法可以完美解决这个问题 <div class=& ...

  4. 设计能长按并有动画效果且能触发事件的高级view

    设计能长按并有动画效果且能触发事件的高级view 效果图: 源码: LongTapAnimationView.h 与 LongTapAnimationView.m // // LongTapAnima ...

  5. vue中methods中的方法闭包缓存问题

    vue中methods中的方法闭包缓存问题 问题背景 需求描述 在路由的导航栏中需要, 判断是否为第一次点击 需要一个标志位来记录是否点击过 现状: 这个标志位只在一个函数中用过.不希望存放全局 希望 ...

  6. vue 父子组件传值以及方法调用,平行组件之间传值以及方法调用大全

    vue项目经常需要组件间的传值以及方法调用,具体场景就不说了,都知道.基本上所有的传值都可以用vuex状态管理来实现,只要在组件内监听vuex就好. vue常用的传值方式以及方法有: 1. 父值传子( ...

  7. Vue 事件监听实现导航栏吸顶效果(页面滚动后定位)

    Vue 事件监听实现导航栏吸顶效果(页面滚动后定位) Howie126313 关注 2017.11.19 15:05* 字数 100 阅读 3154评论 0喜欢 0 所说的吸顶效果就是在页面没有滑动之 ...

  8. vue事件修饰符(once:prev:stop)

    vue事件修饰符(once:prev:stop) stop修饰符  效果如下: 当你鼠标在这个div里的时候,x与y的值:会随着鼠标的变化而变化.但是当鼠标放在stopMoving的时候,x与y的值是 ...

  9. Vue事件绑定原理

    Vue事件绑定原理 Vue中通过v-on或其语法糖@指令来给元素绑定事件并且提供了事件修饰符,基本流程是进行模板编译生成AST,生成render函数后并执行得到VNode,VNode生成真实DOM节点 ...

随机推荐

  1. Properties类的使用 序列化与反序列化

    Properties 类表示了一个持久的属性集.Properties 可保存在流中或从流中加载.属性列表中每个键及其对应值都是一个字符串. 特点: 1.Hashtable的子类,map集合中的方法都可 ...

  2. MySQL数据的增删改查查查查查查查查查查查查查查查查(查是真的多)

    一 数据的增加 主要是运用insert  into 语句. 格式: insert  into 表名称 values(数据,数据,数据)(要按顺序来,有没有数据的可以加null) 只增加某些字段里数据的 ...

  3. leetcode 877. Stone Game 详解 -——动态规划

    原博客地址 https://blog.csdn.net/androidchanhao/article/details/81271077 题目链接 https://leetcode.com/proble ...

  4. excel表格,根据某一列的值对整行进行颜色填充

    1.选中要影响的表格范围,选择 “条件格式”,选择 “新建规则” (2)选择 “使用公式确定要设置格式的单元格”,录入公式,选择 “ 格式”,注意: 公式为:=$H1="待解决" ...

  5. 更优雅的配置:docker/运维/业务中的环境变量

    目录 docker-compose 环境变量 .env 文件 env_file docker stack 不支持基于文件的环境变量 envsubst envsubst.py 1. 使用行内键值对 2. ...

  6. Docker商业版受限,胖容器是个选择

    前情概要 8月13日,Docker 公司更新了网站服务协议,条款申明,禁止禁运国家和被列入「美国实体清单」的组织和个人使用带有该服务协议链接的 Docker 网站和所有相关网站.这一更新协议迅速引起了 ...

  7. anaconda3 安装pip3

    事先安装了anaconda3版本,此时想要安装pip3,需要: https://bootstrap.pypa.io/get-pip.py 打开链接,将文本存到本地,命名为get_pip.py. 然后通 ...

  8. python 列表和字典的引用与复制(copy)

    列表或字典的引用: 引用针对变量的时候,传递引用后,对引用后的对象的值进行改变是不会影响到原值的:而列表不一样如: spam =42 cheese = spam spam =100 print(spa ...

  9. muduo源码解析3-currentthread命名空间

    CurrentThread 作用: CurrentThread并不是一个类,而是一个命名空间,在mymuduo内部,目的是提供对于当前线程的管理操作. 内部变量: __thread int t_cac ...

  10. 曹工改bug:cpu狂飙,old gc频繁,线程神秘死亡连环案件调查报告

    曹工改bug:cpu狂飙,old gc频繁,线程神秘死亡连环案件调查报告 前言 前两天,访问开发环境上一个java服务,发现一直转圈圈,因为我开着fiddler,可以看到的现象是,接口一直没返回:本来 ...