在刚出来找工作的时候,其实Vue还是学的一知半解,在面试的时候频繁被问到VUE组件间通信的问题,那时候没有做过项目也不是很了解更没有认真背面试八股文,所以面试的场面一度很尴尬。
在谈组件间通信时,一定要先明白一件事,就是我们前端就是在合适的时候通过合适的方法获取数据,再把数据展示在合适的位置。所以组件间通信是在大型项目开发过程中基础的基础,最最重要的东西。
我在下面总结了一下五种组件间通信的方式并做了总结,大佬可以忽略,小白的话,背一下方法,琢磨琢磨代码,面试的时候一定有帮助。
1-在父组件的子标签中动态绑定需要传递的值 如 :todos=“todos”
2-在子组件中通过props接收父组件传递过来的数据 如下 props:[ ’ todos ’ ]
3-然后子组件中就可以直接使用父组件传递过来的数据了。
//父组件
<template>
<div>
<son :todos="todos"></son>
</div>
</template>
<script>
import Son from './components/Son'
export default {
name:'App',
components:{Son},
data(){
return{
todos:[
{id:'0001',title:'吃饭',done:true},
{id:'0002',title:'睡觉',done:false},
{id:'0003',title:'打代码',done:true}
]
}
}
}
</script>
//子组件
<template>
<div v-for="todoObj in todos">
{{todoObj}}
</div>
</template>
<script>
export default {
name:'Son',
props:['todos']
}
</script>
2.子组件给父组件传值(通过调用函数传递参数)
1-父组件中写一个方法:fun1(),并动态绑定到父组件中的子组件标签上(即父组件为子组件传递一个方法fun1())。
2-在子组件中通过props接收父组件传过来的方法fun1()。
3-在子组件中写一个方法:fun2(),当该方法被触发时,在该方法中调用fun1(),并将想要给父组件传递的数据以fun1的参数的形式传过去。
4-在父组件的方法fun1中接收子组件传递过来的参数并使用。
//父组件
<template>
<div>
//addTodo为绑定的方法
<son :addTodo="addTodo"></son>
</div>
</template>
<script>
import Son from './components/Son'
export default {
name:'App',
components:{Son},
data(){
return{
todos:[
{id:'0001',title:'吃饭',done:true},
{id:'0002',title:'睡觉',done:false}
]
}
},
methods:{
//添加一个todo对象,todoObj为接收的子组件传过来的数据
addTodo(todoObj){
this.todos.unshift(todoObj)
},
}
}
</script>
//子组件
<template>
<div>
<input v-model="title" @keyup.enter="add"></input>
</div>
</template>
<script>
export default {
name:'Son',
props:['addTodo'],
data(){
return{
title:''
}
},
methods:{
add(){
//将用户输入包装成一个对象
const todoObj = {id:nanoid(),title:this.title,done:false}
//通知App组件接收一个新生成的todoObj对象
this.addTodo(todoObj)
},
}
}
</script>
3.兄弟传值(大儿子son1—>父亲—>小儿子son2)
二:通过自定义事件实现子组件给父组件传值
1.使用v-on(@)为父组件中的子标签绑定一个自定义事件
1-为子组件绑定一个事件,该事件被触发时调用一个函数,该函数中通过 this.$emit ( xxxxx , yyyyy ) ,去触发父组件的子组件标签上绑定的自定义事件。其中xxxxxx为自定义的事件名,yyyyy为想要为父组件传递的参数。
2-在父组件中的子组件标签上通过v-on或者@绑定一个自定义事件,事件名为xxxxx(与子组件中emit触发的事件名一致),该自定义事件被触发时调用一个方法, 可以在该方法中以参数的形式接收$emit传递过来的参数,即实现了子组件向父组件传值。
注意:组件上绑定原生DOM事件,需要使用native修饰符
//子组件
<template>
<div class="student">
<h2 >学生姓名:{{name}}</h2>
<button @click="sendStudentName">把学生名给App</button>
<button @click="unbind">解绑peiqi事件</button>
</div>
</template>
<script>
export default {
name:'Student',
data(){
return{
name:'张三'
}
},
methods:{
sendStudentName(){
//触发Student子组件标签身上的peiqi事件
this.$emit('peiqi',this.name)
//触发Student子组件标签身上的demo事件
this.$emit('demo')
//原生demo *** 作
this.$emit('click')
},
unbind(){
this.$off('peiqi')//解绑一个自定义事件
this.$off(['peiqi','demo']) // 解绑多个自定义事件
this.$off() //解绑所有自定义事件
}
}
}
</script>
//父组件
<template>
<div class="app">
<h1>{{msg}},学生姓名是:{{studentName}}</h1>
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on) -->
<Student @peiqi="getStudentName" @demo="m1" @click.native="show"/>
</div>
</template>
<script>
import Student from './components/Student'
export default {
name:'App',
components:{Student},
data() {
return {
msg:'你好啊!',
studentName:''
}
},
methods: {
getStudentName(name,...params){
console.log('App收到了学生名:',name,params)
this.studentName = name
},
m1(){
console.log('demo事件被触发了!')
},
show(){
alert('123')
}
},
}
</script>
2.使用ref为父组件中的子标签绑定一个自定义事件
1-通过ref标记子组件标签,使用this.$refs.student获取子组件的整个实例对象,并通过$on为其绑定一个自定义事件,事件的回调留在父组件中。
2-为子组件绑定一个事件,该事件被触发时调用一个函数,该函数中通过 this.$emit ( xxxxx , yyyyy ) ,去触发父组件中通过$refs为子组件标签绑定的自定义事件,同时触发回调。
//子组件
<template>
<div class="student">
<h2 >学生姓名:{{name}}</h2>
<button @click="sendStudentName">把学生名给App</button>
</div>
</template>
<script>
export default {
name:'Student',
data(){
return{
name:'张三'
}
},
methods:{
sendStudentName(){
//触发Student子组件标签身上的peiqi事件
this.$emit('peiqi',this.name)
},
unbind(){
this.$off('peiqi')//解绑一个自定义事件
}
}
}
</script>
//父组件
<template>
<div class="app">
<h1>{{msg}},学生姓名是:{{studentName}}</h1>
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref) -->
<Student ref="student"/>
</div>
</template>
<script>
import Student from './components/Student'
export default {
name:'App',
components:{Student},
data() {
return {
msg:'你好啊!',
studentName:''
}
},
methods: {
getStudentName(name,...params){
console.log('App收到了学生名:',name,params)
this.studentName = name
}
},
mounted() {
this.$refs.student.$on('atguigu',this.getStudentName) //绑定自定义事件
// this.$refs.student.$once('atguigu',this.getStudentName) //绑定自定义事件(一次性)
},
}
</script>
<style scoped>
</style>
三:通过全局事件总线实现任意组件间通信(重点)
先了解一下什么是事件总线:
假设有一个X 它不属于任何一个组件,如果组件B想要给组件A传递数据,就可以在组件A 中给这个 X 通过$on绑定自定义事件demo,组件B通过$emit触发这个自定义demo事件以携带参数的方式传递数据,同时A组件中调用了回调函数接收数据。
要实现这些就需要X满足一下条件:
1-所有的组件都可以触碰到这个X
2-X可以调用到$on $off $emit 绑定 解绑 触发…
我们可以通过把X挂载到vue的原型对象上来实现这一功能,因为在console.log(Vue.prototype)时可以发现,它内置的方法包含$on $off $emit 等。挂载之后,全局就都可以看到X了。
对于X我们程序员们有一个约定俗成的名字:$bus。
1-安装全局事件总线:在入口文件main.js中,new Vue的时候,使用beforeCreate钩子,在vue原型上面挂载全局事件总线。
2-在需要传递数据的组件中写入:this.$bus.$emit(‘事件名’,传递的数据)
3-在需要接收数据的组件中写入:this.$bus.$on(‘事件名’,调用函数(写成箭头函数或回调函数))
注意:在接收数据的组件销毁之前要将绑定的事件解绑。
//入口文件main.js
// 创建vm
new Vue({
el: '#app',
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线
},
})
//student组件
<template>
<div class="student">
<h2 >学生姓名:{{name}}</h2>
<button @click="sendStudentName">把学生名给School组件</button>
</div>
</template>
<script>
export default {
name:'Student',
data(){
return{
name:'张三'
}
},
methods:{
sendStudentName() {
this.$bus.$emit('hello',this.name)
}
}
}
</script>
//school组件
<template>
<div class="school">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</template>
<script>
export default {
name:'School',
data(){
return{
name:'霍格沃兹魔法学院',
address:'北京'
}
},
methods:{
demo(data) {
console.log('我是School组件,收到了数据',data);
}
},
mounted() {
//写法一
// this.$bus.$on('hello',(data) => {
// console.log('我是School组件,收到了数据',data);
// })
//写法二
this.$bus.$on('hello',this.demo)
},
beforeDestroy() {
this.$bus.$off('hello') //解绑事件
}
}
</script>
<style scoped>
</style>
四:通过消息订阅与发布实现任意组件间通信
与全局事件总线类似,但是是通过借助外部引入的pubsub库实现组件通信的。与全局事件总线相比,实际工作中最常用的还是全局事件总线
//student组件发布消息
<template>
<div class="student">
<h2 >学生姓名:{{name}}</h2>
<button @click="sendStudentName">把学生名给School组件</button>
</div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
name:'Student',
data(){
return{
name:'张三'
}
},
methods:{
//发布消息
sendStudentName() {
pubsub.publish('hello',666)
}
}
}
</script>
//school组件订阅消息
<template>
<div class="school">
<h2>学校名称:{{name}}</h2>
</div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
name:'School',
data(){
return{
name:'霍格沃兹魔法学院'
}
},
mounted() {
//订阅消息时会接收到两个参数,一个是消息名,一个是发布消息者传递的数据,所以需要用msgName接收一下。
this.pubId = pubsub.subscribe('hello',(msgName,data) => {
// 注意this的指向问题,要写成箭头函数
console.log('有人发布了hello消息,hello消息的回调执行了',msgName,data);
})
},
//要在组件销毁之前取消订阅的内容
beforeDestroy() {
pubsub.unsubscribe(this.pubId)
}
}
</script>
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)