由于本人才疏学浅本文章参考了诸多大神的想法,会在文末贴上原文地址
$on && $ emit
$emit(eventName, [...args])
:触发事件$on(eventName, callBack)
:监听事件
监听当前实例上的自定义事件,可以由vm.$emit
触发。回调函数会接受所有传入事件出发函数的额外参数。
如果把Vue
看成一个家庭(相当于一个单独的components
),女主人一直在家里指派($emit)
男人做事,而男人则一直监听($on)
着女士的指派($emit)里eventName
所触发的事件消息,一旦 $emit
事件一触发,$on
则监听到 $emit
所派发的事件,派发出的命令和执行派执命令所要做的事都是一一对应的。
1 | <template> |
如本例所示,我们可以将要监听的事件统一写到一个数组中,方便我们做一个类似于过滤器的统一处理。比如例子里的this.$on(['wash_Goods','drive_Car'],(arg)=> { console.log('事真多')})
就类似于过滤器之类的机制,可以对“麻烦的女人”指派(emit
)的事件进行一个统一的监听(on
)并处理(****真麻烦!
)。
上述例子中就是相当于通过this.$emit('wash_Goods', 'fish')
给男人一个手册,告诉男人东西放在哪里,需要什么工具等等。
我们平时在开源库里使用的框架中都有无限下拉组件,那么我们一起跟随大神“混元霹雳手”的视角去看一下这类组件的实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63<template>
<div>
<slot name="list"></slot>
<div class="list-donetip" v-show="!isLoading && isDone">
<slot>没有更多数据了</slot>
</div>
<div class="list-loading" v-show="isLoading">
<slot>加载中</slot>
</div>
</div>
</template>
<script type="text/babel">
export default {
data() {
return {
isLoading: false,
isDone: false,
}
},
props: {
onInfinite: {
type: Function,
required: true
},
distance : {
type : Number,
default:100
}
},
methods: {
init() {
this.$on('loadedDone', () => {
this.isLoading = false;
this.isDone = true;
});
this.$on('finishLoad', () => {
this.isLoading = false;
});
},
scrollHandler() {
if (this.isLoading || this.isDone) return;
let baseHeight = this.scrollview == window ? document.body.offsetHeight : this.scrollview.offsetHeight
let moreHeight = this.scrollview == window ? document.body.scrollHeight : this.scrollview.scrollHeight;
let scrollTop = this.scrollview == window ? document.body.scrollTop : this.scrollview.scrollTop
if (baseHeight + scrollTop + this.distance > moreHeight) {
this.isLoading = true;
this.onInfinite()
}
}
},
mounted() {
this.scrollview = window
this.scrollview.addEventListener('scroll', this.scrollHandler, false);
this.$nextTick(this.init);
},
}
</script>
对下拉组件加载加更的组件进行了一个简单的封装:
1 | data 参数解释: |
- isLoading
false 代表正在执行下拉加载获取更多数据的标识
,true代表数据加载完毕
- isDone
false 代表数据没有全完加载完毕
,true 代表数据已经全部加载完毕
1 | props 参数解释: |
- onInfinite
父组件向子组件传入当滚动到底部时执行加载数据的函数
- distance
距离滚动到底部的设定值
从此组件中,我们进行每一步的分析
在
mounted
的时候,对window
对像进行了一个滚动监听,监听的函数为scrollHandler
当
isLoading,isDone
任何一个为true时则退出isloading
为true
时防止多次同样加载,必须等待加载完毕isDone
为true
时说明所有数据已经加载完成,没有必要再执行scrollHandler
同时在$nextTick中进行了初始化监听
loadedDone
一旦组件实例$emit(‘loadedDone’)事件时,执行回调,放开加载权限finishLoad
一旦组件实例$emit(‘finishLoad’)事件时,执行回调,放开加载权限
再看看 scrollHandler函数里发生了什么
if (this.isLoading || this.isDone) return;
一旦一者为true,则退出,原因在mounted已经叙述过了1
if (baseHeight + scrollTop + this.distance > moreHeight)
当在window对象上监听scroll事件时,当滚动到底部的时候执行
this.isLoading = true;
防止重复监听this.onInfinite()
执行加载数据函数
父组件中调用infinite-scroll
组件
1 | <template> |
当滑到底部的时候,infinite-scroll 组件组件内部会执行传入的:on-infinite='loadData'
函数 同时在内部也会把 Loading 设置为 true,防止重复执行。
在这里用this.$refs.infinite
拿到infinite-scroll
组件的实例,同时触发事件之前在组件中 $on
已经监听着的事件,在一秒后进行改变数据,同时发出loadDone
事情,告诉组件内部去执行loadDone
的监听回调,数据已经全部加载完毕,设置this.isDone = true;
一旦isDone
或者isLoading
一者为true
,则一直保持return退出状态
。
$emit 和 $on 必须都在实例上进行触发和监听。
组件解释
至于解释部分嘛,有兴趣的boy可以移步另一篇博客《Vue中实现下拉加载组件》)
v-on
v-on
作用在引入子组件后的父组件模板,用来监听子组件释放的事件
$on
和$emit
则只能作用在事件名一一对应的同一个组件实例中
父组件中的“v-on:”
之后绑定事件名+回调函数1
<demo v-on:eventName="callBack"></demo>
而子组件中则通过绑定触发事件的函数来将消息传回至父组件中(以此来触发父组件的回调函数)
个人理解是,所谓的信息传递只不过是事件被触发后进行的某事件名的传递,以告诉父组件此事件被触发,之后父组件则得到此消息(主要是事件名)确定了此事件绑定的回调函数,并对其进行调用
1
2
3
4
5
6
7
8
9
10
11 <template>
<button @click="emit">点我给父组件传递消息</button>
</template>
export default {
name: 'demo',
methods: {
emit() {
this.$emit('eventName')
}
}
}
我们举个栗子
1 | <div id="counter-event-example"> |
如此例所示,在counter-event-example
父组件里,声明了两个button-count
的实列,通过 data
用闭包的形式,让两者的数据都是单独享用的,而且v-on
所监听的 eventName
都是当前自己实列中的 $emit
触发的事件,但是回调都是公用的一个 incrementTotal
函数,因为个实例所触发后都是执行一种操作!
PS:
v-on
声明在父组件中,你可以理解为一直抱着手机等你女神回消息的你:<waiting v-on:call-repair="repair"></waiting>
。emit
则在子组件中,一般通过某些动作触发调用对应函数,然后完成对信息的传递。比如你女神需要你给她修电脑的时候,她会在你俩尘封已久的聊天框上click
一下:<div @click="callRepair">会修电脑的人</div>
,而她的methods
中必定有一个名为call-repair
的方法,通过this.$emit()
【没错,人家什么东西(参数)都不会给你,只会单纯的派遣任务,让你知道call-repair
事件被触发,需要你调用你的repair
方法】让你触发repair
方法完成操作。
说到最后话题有点悲伤,不过也告诉我们相处应该讲究方法,如果你一开始将其视为自己的“女神(子组件)”百般呵护,单向输出,也怪不得人家调用$emit
,只在用得到你的时候触发。
下一章我们会讲到更加“健康”的关系:v-model
。双向沟通,相濡以沫才是正确的方式
最后,感谢掘金的“混元霹雳手”大大,他的博客给了我非常多的灵感,并且我引用了他的2个例子。大佬博客传送门在这里。
今天就到这里啦