Vue组件间通信的几种方式

Vue组件间通信的几种方式,第1张

写在前面:

在刚出来找工作的时候,其实Vue还是学的一知半解,在面试的时候频繁被问到VUE组件间通信的问题,那时候没有做过项目也不是很了解更没有认真背面试八股文,所以面试的场面一度很尴尬。
在谈组件间通信时,一定要先明白一件事,就是我们前端就是在合适的时候通过合适的方法获取数据,再把数据展示在合适的位置。所以组件间通信是在大型项目开发过程中基础的基础,最最重要的东西。
我在下面总结了一下五种组件间通信的方式并做了总结,大佬可以忽略,小白的话,背一下方法,琢磨琢磨代码,面试的时候一定有帮助。

一:传统方式的组件传值 1.父组件给子组件传值(通过props的方式)


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>

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1298658.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-10
下一篇 2022-06-10

发表评论

登录后才能评论

评论列表(0条)

保存