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. C#LeetCode刷题之#56-合并区间(Merge Intervals)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3676 访问. 给出一个区间的集合,请合并所有重叠的区间. 输入: ...

  2. cocos2dx重新设置 SDK NDK目录

    参考博客https://blog.csdn.net/yinhe888675/article/details/41042347 初始设置SDK,NDK目录后,当我想换目录的时候不知道该怎么办,怎么重新设 ...

  3. JDK8 String类知识总结

    一.概述 java的String类可以说是日常实用的最多的类,但是大多数时候都只是简单的拼接或者调用API,今天决定深入点了解一下String类. 要第一时间了解一个类,没有什么比官方的javaDoc ...

  4. JNDI和连接池的配置

    什么是JNDI: Java Naming and Directory Interface,Java命名和目录接口 通过名称将资源与服务进行关联 配置JNDI的步骤:在tomcat下的Context.x ...

  5. SpringMVC大威天龙

    一 SpringMVC简介 SpringMVC是Spring提供的一个强大而灵活的Web框架 借助于注解 SpringMVC提供了几乎是POJO的开发模式 使得控制器的开发和测试更加简单 二 Spri ...

  6. CVT1100 错误的修复 2009-10-12 11:38

    我们在用VS2005编译MFC工程时极少会出现如下错误: 一,CVTRES : fatal error CVT1100: 重复的资源.type:MANIFEST, name:1, language:0 ...

  7. 关于Exceptionless日志收集框架会被Fiddler抓包,从而获取到ApiKey的问题

    关于Exceptionless日志收集框架会被Fiddler抓包,从而获取到ApiKey的问题 环境:Exceptionless5.0.0.Linux.WPF客户端.Fiddler 问题:在使用Exc ...

  8. C++统计单词数

    [题目描述] 一般的文本编辑器都有查找单词的功能,该功能可以快速定位特定单词在文章中的位置,有的还能统计出特定单词在文章中出现的次数.现在,请你编程实现这一功能,具体要求是:给定一个单词,请你输出它在 ...

  9. JavaScript学习系列博客_25_JavaScript 数组(Array)

    数组 - 数组也是一个对象,是一个用来存储数据的对象,和Object类似,但是它的存储效率比普通对象要高. - 数组中保存的内容我们称为元素 - 数组使用索引(index)来操作元素 - 索引指由0开 ...

  10. 第7章 Spark SQL 的运行原理(了解)

    第7章 Spark SQL 的运行原理(了解) 7.1 Spark SQL运行架构 Spark SQL对SQL语句的处理和关系型数据库类似,即词法/语法解析.绑定.优化.执行.Spark SQL会先将 ...