13.组件之间的通信方式
组件之间的通信方式
13.1 props
父子组件间通信的基本方式
属性值的 2 大类型:
一般: 父组件-->子组件
函数: 子组件-->父组件
隔层组件间传递: 必须逐层传递(麻烦)
兄弟组件间: 必须借助父组件(麻烦)
13.1.1 声明
在组件内声明所有的 props
方式一: 只指定名称
props:['name','age','setName']
方式二: 指定名称和类型
props:{ name:String, age:Number, setName:Function }
方式三: 指定名称/类型/必要性/默认值
props:{ name:{ type:String, required:true, default:xxx }, }
13.1.2 优缺点
此方式用于父组件向子组件传递数据
所有标签属性都会成为组件对象的属性, 模板页面可以直接引用
问题:
a. 如果需要向非子后代传递数据必须多层逐层传递
b. 兄弟组件间也不能直接 props 通信, 必须借助父组件才可以
13.2 自定义事件
子组件也无法使用组件的事件,要想传递给父组件事件,需要通过内置的$emit 方法并传入事件的名字,来向父级组件触发一个事件
13.2.1 $emit()向子组件传递事件
在子组件的引用中绑定事件的自定义名字,然后绑定父组件的事件,在子组件中通过抛出事件$emit 来使用父组件的事件
<!--引用子组件-->
<blog-post v-on:enlarge-text="doSomething"></blog-post>
<!--子组件-->
<script>
Vue.component("enlarge-text",{
template:"<button v-on:click="$emit('enlarge-text')">Enlarge text</button>"
})
</script>
13.2.2 $emit()向父组件抛出值
子父组件的函数中写入参数,那么当子组件使用$emit 的时候可以传入后面多个参数来将值传递给父组件的参数,从而实现子组件向父组件传值
<div id="app">
<blog-post v-on:enlarge-text="onEnlargeText"></blog-post>
</div>
<script>
Vue.component("blog-post",{
template:"<button v-on:click="$emit('enlarge-text',0.1)">Enlarge text</button>"
})
new Vue({
el="#app",
data:{
postFontSize:1
}
methods: {
onEnlargeText: function (enlargeAmount) {
this.postFontSize += enlargeAmount
}
}
})
</script>
**注意:**虽然子组件能触发父组件的方法或值,但是这些方法中的 this 指向依然是父组件,子组件只是使用的这些方法,同时传递了一些参数
13.2.3 实例
<template>
<div class="todo-container">
<div class="todo-wrap">
<!-- 需要自定义一个事件,事件名最好与对应方法名一致 -->
<!-- 在子组件调用的地方使用,即可调用
this.$emit('addItem', this.value)
参数格式:自定义事件名,后面是要跟参数 -->
<myHeader @addItem="addItem"></myHeader>
</div>
</div>
</template>
<script>
import myHeader from "./myHeader";
export default {
data() {
return {
// 读取localStorage保存的数据
items: JSON.parse(localStorage.getItem("todos_key") || "[]"),
};
},
components: {
myHeader,
},
methods: {
addItem(newValue) {
const newItem = { name: newValue, complete: false };
this.items.push(newItem);
},
},
watch: {
items: {
deep: true,
handler: function(val) {
// 将数据(json)保存到localStorage
localStorage.setItem("todos_key", JSON.stringify(val));
},
},
},
};
</script>
<style></style>
13.2.4 $on 与$once
- $on 属性用于监听在当前实例上的自定义事件,事件可以由 vm.$emit 触发。回调函数会接收所有传入事件触发函数的额外参数
- $once 属性与$on 一致,只不过只会触发一次,触发一次后移除该事件
vm.$on("test", function(msg) {
console.log(msg);
});
vm.$once("test", function(msg) {
console.log(msg);
});
vm.$emit("test", "hi");
// => "hi"
**注意:**在组件化中通过创建一个中间的 Vue 实例可以进行中转完成父子组件的事件传值
13.2.5 $on()实例
<template>
<div class="todo-container">
<div class="todo-wrap">
<!-- 要给组件一个ref,在接下来进行绑定 -->
<myHeader ref="myhead"></myHeader>
</div>
</div>
</template>
<script>
import myHeader from "./myHeader";
export default {
data() {
return {
// 读取localStorage保存的数据
items: JSON.parse(localStorage.getItem("todos_key") || "[]"),
};
},
components: {
myHeader,
}, // 需要在mouunted()中使用$on,理由是要在程序还没有开始的时候就挂载该事件 // 确保其总能被触发
mounted() {
// 哪个子组件要,就给谁$on
// 不能写成this.$on('addItem', this.addItem)
// 参数为自定义事件名,要使用的方法
this.$refs.head.$on("addItem", this.addItem);
},
methods: {
addItem(newValue) {
const newItem = { name: newValue, complete: false };
this.items.push(newItem);
},
},
watch: {
items: {
deep: true,
handler: function(val) {
// 将数据(json)保存到localStorage
localStorage.setItem("todos_key", JSON.stringify(val));
},
},
},
};
</script>
<style></style>
13.2.6 总结
方式一:
通过
v-on
绑定@delete_todo="deleteTodo"
触发事件(只能在父组件中接收)
this.$emit(eventName,data)
方式二:
通过
$on() this.$refs.xxx.$on('delete_todo', function(todo){ this.deleteTodo(todo) }) )
此方式只用于子组件向父组件发送消息(数据),用来取代 function props(用来取代方法)
问题: 隔代组件或兄弟组件间通信此种方式不合适
13.3 发布-订阅通信机制
13.3.1 pubsub-js 的下载引入
使用消息订阅(subscribe)-发布(publish)机制: 自定义事件机制
工具库 : PubSubJS
下载: npm install pubsub-js --S --D
使用:
import PubSub from 'pubsub-js' //引入
PubSub.subscribe('delete', function(msg, data){ }); //订阅
PubSub.publish('delete', data) //发布消息
优点: 可以支持任意关系组件之间的通信
13.3.2 发布消息
发布------触发事件监听
PubSub.publish('msg',data)
msg:给本次发布-订阅取的名字
data:需要传递的参数
13.3.3 订阅消息
订阅------绑定事件监听
PubSub.subscribe('msg',
(msg,data) => {
})
msg: 对应发布时的名字
data: 发布时传递过来的参数
回调函数: 处理的方法,再此建议用箭头函数,避免使用 this 的时候指向错误
13.3.4 实例
// 父组件
<template>
<div class="todo-container">
<div class="todo-wrap">
<!-- 需要给它的子组件(也就是这个组件的父组件)绑定一个remove(index)方法 -->
<myMain :items="items"></myMain>
</div>
</div>
</template>
<script>
import myMain from './myMain'
import Pubsub from 'pubsub-js'
export default {
data () {
return {
// 读取localStorage保存的数据
items: JSON.parse(localStorage.getItem('todos_key') || '[]')
}
},
components: {
myMain
},
mounted () {
// 在此进行发布,理由是要在程序还没有开始的时候就挂载该事件,确保其总能被触发
// 取名为delItems,msg不可省略,指代delItems,index为参数
Pubsub.subscribe('delItems', (msg, index) => {
// 用箭头函数使得this指向该实例
this.remove(index)
})
},
methods: {
remove (index) {
this.items.splice(index, 1)
}
}
}
</script>
<style>
</style>
13.4 Slot 传递标签
13.4.1 理解
此方式用于父组件向子组件传递标签数据
13.4.2 用法
子组件 Child.vue
<template>
<div>
<slotname="xxx">不确定的标签结构 1</slot>
<div>组件确定的标签结构</div>
<slotname="yyy">不确定的标签结构 2</slot>
</div>
</template>
父组件 Parent.vue
<child>
<div slot="xxx">xxx 对应的标签结构</div>
<div slot="yyy">yyyy 对应的标签结构</div>
</child>