1.父子组件之间的通信 props 和 $emit
父组件通过 prop 给子组件下发数据,子组件通过$emit触发事件给父组件发送消息,即 prop 向下传递,事件向上传递。
父组件:parents
<template>
<div>
<children :msg="msg" @say="childrenSay"></children>
</div>
</template>
<script>
import Children from './children'
export default {
name: "parents",
components:{
children:Children
},
data(){
return{
msg:'亲爱的小孩 !',
}
},
methods:{
childrenSay(msg){
console.log(msg)
}
}
}
</script>
子组件 :children
<template>
<div>
<p v-text="'父组件对我说:'+msg"></p>
<p @click="say" style="border:1px solid #eee;">我对父组件说的话</p>
</div>
</template>
<script>
export default {
name: "children",
props:{
msg:{
type:String,
default: () => {
return ''
}
}
},
data(){
return{
childrenSay: '亲爱的父亲 !'
}
},
methods:{
say(){
this.$emit('say' , this.childrenSay);
}
}
}
</script>
效果如下:
2. a t t r s 和 attrs和 attrs和listeners 的使用
A组件
<template>
<div>
<child-dom
:foo="foo"
:coo="coo"
v-on:upRocket="reciveRocket">
</child-dom>
</div>
</template>
<script>
import childDom from "./childDom";
export default {
name:'demoNo',
data() {
return {
foo:"Hello, world",
coo:"Hello,rui"
}
},
components:{childDom},
methods:{
reciveRocket(){
console.log("reciveRocket success")
}
}
}
</script>
B组件
<template>
<div>
<p class="childDom">foo:{{foo}}</p>
<p class="childDom">attrs:{{$attrs}}</p>
<childDomChild v-bind="$attrs" v-on="$listeners"></childDomChild>
</div>
</template>
<script>
import childDomChild from './childDomChild';
export default {
components:{childDomChild},
name:'child-dom',
props:["foo"],
inheritAttrs:false,
}
</script>
c组件
<template>
<div>
<p>coo:{{coo}}</p>
<button @click="startUpRocket">我要发射火箭</button>
</div>
</template>
<script>
export default {
name:'childDomChild',
props:['coo'],
methods:{
startUpRocket(){
this.$emit("upRocket");
}
}
}
</script>
总结
a
t
t
r
s
包
含
了
父
作
用
域
中
不
被
认
为
(
且
不
预
期
为
)
p
r
o
p
s
的
特
性
绑
定
(
c
l
a
s
s
和
s
t
y
l
e
除
外
)
。
当
一
个
组
件
没
有
声
明
任
何
p
r
o
p
s
时
,
这
里
会
包
含
所
有
父
作
用
域
的
绑
定
(
c
l
a
s
s
和
s
t
y
l
e
除
外
)
,
并
且
可
以
通
过
v
−
b
i
n
d
=
”
attrs 包含了父作用域中不被认为 (且不预期为) props 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 props 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=”
attrs包含了父作用域中不被认为(且不预期为)props的特性绑定(class和style除外)。当一个组件没有声明任何props时,这里会包含所有父作用域的绑定(class和style除外),并且可以通过v−bind=”attrs” 传入内部组件——在创建更高层次的组件时非常有用。
$listeners
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=”$listeners” 传入内部组件——在创建更高层次的组件时非常有用。
inheritAttrs
默认情况下父作用域的不被认作 props 的特性绑定 (attribute bindings) 将会“回退”且作为普通的 HTML 特性应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrs 到 false,这些默认行为将会被去掉。而通过 (同样是 2.4 新增的) 实例属性 $attrs 可以让这些特性生效,且可以通过 v-bind 显性的绑定到非根元素上。
上述特性的使用完全可以降低在不使用Vuex以及事件总线的情况下,组件跨级props以及事件传递的复杂度。
3.中央事件总线
一个中央事件总线bus,可以作为一个简单的组件传递数据,用于解决跨级和兄弟组件通信问题,那么我们将使用这种思想,将bus封装为一个Vue的插件,可以在所有的组件间任意使用,而不需要导入bus。
首先在src目录下新建vue-bus.js
const install = function (Vue) {
const Bus = new Vue({
methods: {
emit (event, ...args) {
this.$emit(event, ...args);
},
on (event, callback) {
this.$on(event, callback);
},
off (event, callback) {
this.$off(event, callback);
}
}
});
Vue.prototype.$bus = Bus;
};
export default install;
其次在main.js中引用
import VueBus from './vue-bus';
Vue.use(VueBus); //加载插件VueBus
然后新建一个counter组件
<template>
<div>
{{ number }}
<button @click="handleAddRandom">随机增加</button>
</div>
</template>
<script>
export default {
props: {
number: {
type: Number
}
},
methods: {
handleAddRandom () {
// 随机获取 1-100 中的数
const num = Math.floor(Math.random () * 100 + 1);
this.$bus.emit('add', num);
}
}
};
</script>
说明
1)定义一个按钮,组件有个属性名称是number;
2)组件定义了一个方法,随机去一个数据,会调用bus定义的一个时间,并将数据传递过去;
最后index.vue
<template>
<div>
<h1>首页</h1>
随机增加:
<Counter :number="number"></Counter>
</div>
</template>
<script>
import Counter from './counter.vue';
export default {
components: {
Counter
},
computed: {
.....
},
methods: {
.......
handleAddRandom (num) {
this.number += num;
}
},
data () {
return {
number: 0
}
},
created () {
this.$bus.on('add', this.handleAddRandom);
},
beforeDestroy () {
this.$bus.off('add', this.handleAddRandom);
}
}
</script>
效果如下
说明
1)导入自定义的组件,计数器;
2)组件创建和销毁后,分别绑定和基础自定义的事件add,即对add事件进行监听
3)接受触发add事件后,接受到传递过来的数据;
//
使用vue-bus有两点需要注意,第一是
b
u
s
.
o
n
应
该
在
c
r
e
a
t
e
d
钩
子
内
使
用
,
如
果
在
m
o
u
n
t
e
d
使
用
,
它
可
能
接
收
不
到
其
他
组
件
来
自
c
r
e
a
t
e
d
钩
子
内
发
出
的
事
件
;
第
二
点
是
使
用
了
bus.on应该在created钩子内使用,如果在mounted使用,它可能接收不到其他组件来自created钩子内发出的事件;第二点是使用了
bus.on应该在created钩子内使用,如果在mounted使用,它可能接收不到其他组件来自created钩子内发出的事件;第二点是使用了bus.on在beforeDestory钩子里应该需要使用$bus.off解除,因为组件销毁后,就没有必要把监听的句柄存储在vue-bus里面了