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. 2020-04-29:现在你有个秒杀抢购的app,用户不断大量增加,技术层面,你要怎么做

    2020-04-29:现在你有个秒杀抢购的app,用户不断大量增加,技术层面,你要怎么做,才能既满足用户需求,又能扛住压力,还能帮公司合理支出?福哥答案2020-04-29: 限流(杀部分用户祭天). ...

  2. C#算法设计排序篇之08-计数排序(附带动画演示程序)

    计数排序(Counting Sort) 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/689 访问. 计数排序是一个非基 ...

  3. JavaScript map+parseInt 容易产生的误区

    map /** * 语法: * var new_array = arr.map(function callback(currentValue[,index[,array]]){ * // return ...

  4. 利用BeautifulSoup去除HTML指定标签和去除注释

    去除指定标签 from bs4 import BeautifulSoup #去除属性ul [s.extract() for s in soup("ul")] # 去除属性svg [ ...

  5. 将阿里矢量图添加到element-ui

    在阿里矢量图的操作 选择需要的图标添加至购物车   选择图标 将购物车中的图标, 添加至项目   添加至项目 会自动跳转到我的项目   项目页面 在 更多操作 中选择 编辑项目   更多操作 将 Fo ...

  6. 使用动态链接为什么还需要静态库lib文件

    在Windows上使用动态链接时,不光需要头文件 .dll文件 还需要一个.lib 文件. 不是动态链接吗?为什么还需要静态库.lib文件? 实际上,这个.lib文件并不是静态库,而是 导入库 文件, ...

  7. golang 递归自己,输出自己的源代码

    问题: [2min 大家自己想想] 一个程序P运行后能否输出自己的源代码?并且格式保持一致(换行.空格等) 思考: 这个问题的本质是一个递归问题,设有P运行后生成G 既P->G &&am ...

  8. openvswitch 流表操作

    流表组成 每条流表规则由一些列字段组成,可以分为**基础字段.匹配字段和动作字段**三部分. 在打印流表时,在流表中还存在一些显示字段,如duration,idle_age等,此处把这些字段也暂时归之 ...

  9. 什么是RPC,RPC好处,常用的RPC框架

    RPC简介 RPC(Remote Procedure Call Protocol)远程过程调用协议.一个通俗的描述是:客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用 ...

  10. python numpy常用的数学和统计函数

    numpy模块的核心就是基于数组的运算,相比于列表和其他数据结构,数组的运算效率是最高的.在统计分析和挖掘过程中,经常会使用到numpy模块的函数,以下是常用的数学函数和统计函数: 常数p就是圆周率  ...