vue组件之间的数据传递

vue组件之间的数据传递

学习vue的第四天学到了组件之间的数据传递,具体有一下几种需求:

  • 父组件->子组件
  • 子组件->父组件
  • 兄弟组件之间、任意组件之间

实现的方法有如下几种:

  • props传值
  • 自定义事件
  • 事件总线
  • 消息订阅与发布(pubsub-js)

以下是个人理解,如有错误请指正。

props传值

props传值只能实现父组件向子组件传值或者子组件向父组件传值,无法实现兄弟组件之间直接传值,不过这个也是可以实现的,只不过实现起来有点麻烦,需要将数据的状态提升到共同的父组件,再通过层层传递。

所以用到props时一般都是实现的父组件向子组件传值。

父组件->子组件

在vue中父组件向子组件传值非常的方便,父组件只需要将要传递的数据写在组件上,子组件在props属性中接收即可拿到传递的数据,接收之后就可以在this对象中访问到,也就是说可以像访问data中的数据一样进行访问。

示例代码:

// 父组件传值
<MyList :todos="todos" />

// 子组件接收,在
export default {
    props:['todos'],
}
子组件->父组件

利用props最基本的方式,也就是父组件定义一个函数,将函数通过props的方式传递给子组件,这时子组件通过props就可以接收该函数,当子组件想给父组件传递数据的时候直接调用这个函数就可以通过函数参数将数据传递给父组件的函数中,这是父组件拿到了数据,就可以进行下一步的数据处理了。

示例代码:

//父组件
<template>
  <div class="app">
    <MyHeader :handleEnterTodo="handleEnterTodo" />
  </div>
</template>

<script>
    import MyHeader from './components/MyHeader'

    export default {
        name:'App',
        components:{MyHeader},
        data() {
            return{
                todos:[
                    {id:'1', title:'抽烟', done:false},
                    {id:'2', title:'喝酒', done:false},
                    {id:'3', title:'烫头', done:true}
                ]
            }
        },
        methods: {
            //添加todo
            handleEnterTodo(title){
                let id = (this.todos.length + 1).toString()
                let todoObj = {id:id,title:title,done:false}
                this.todos.unshift(todoObj)
            },
        },
    }
</script>
// 子组件
<template>
  <div>
    <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="handleEnter" >
  </div>
</template>

<script>
    export default {
        name:'MyHeader',
        props:['handleEnterTodo'],
        data(){
          return{
            title:'',
          }
        },
        methods: {
          handleEnter(){
            if(this.title.trim() === ''){
              alert("请输入任务")
              return
            }
            this.handleEnterTodo(this.title)
            this.title = ''
          }
        },
    }
</script>

上面父组件向子组件传递了一个函数,这个函数接收一个title参数,然后将title包装成一个对象储存到todos中,子组件通过props接收到函数,当要添加一条信息的时候,将title作为参数传入到的父组件函数中,这时父组件就接收到该值,下面就可以完成添加的需求。

自定义事件

自定义事件可以实现子组件向父组件传值。

子组件->父组件

父组件调用子组件时通过定义一个事件传入子组件,事件不需要子组件去接收什么的,只需要子组件在想调用的时候通过$emit()去出发事件即可。这里涉及到父组件定义事件回调函数、将事件与组件绑定和事件与组件的解绑。

  • 定义回调函数

    自定义一个事件需要创建一个回调函数,当然也可以直接写在绑定事件与组件时的参数中,这里以自定义回调函数为例。

    //在methods中定义一个回调函数
    handleCheckAllTodo(done){
        this.todos.forEach((todo)=>{
            todo.done = done
        })
    },
    
  • 绑定事件

    当事件创建完成之后需要与组件绑定才可以获取到子组件触发事件。绑定事件有两种方式,这里以第一种为例,也就是直接在组件标签中去写。通过v-on:handleCheckAllTodo="handleCheckAllTodo"的方式来绑定一个自定义事件(v-on:可以简写为@符号),引号中的内容为上一步创建的回调函数的名,等号前为自定义事件的名称,也就是在子组件中触发事件时的事件名称。

    <MyFooter
    	:todos="todos"
        @handleCheckAllTodo="handleCheckAllTodo"
    />
    
  • 触发事件

    事件绑定在那个组件中就应该去那个组件中触发,这里到子组件中去触发组件,调用this.$emit('handleCheckAllTodo', data)方法来触发自定义组件,传入的第一个参数时自定义事件的名称,后面跟的参数为事件触发传的参数,也就是传入了父组件的回调函数的参数中。

    handleCheckAll(e){
        this.$emit('handleCheckAllTodo', e.target.checked)
    },
    

上面事件绑定提到还有第二种方法,也就是通过$on()方法来绑定事件

// 首先给要绑定事件的组件添加ref属性,为了下面绑定事件时选择组件
<MyHeader ref="myheader" />
// 自定义事件绑定一般都在组件挂载完毕之后添加
// 通过this.$refs.myheader选中组件,再用$on()绑定事件
// $on()传第一个参数是自定义事件的名称,第二个参数是回调函数
// 也就是在回调函数这里可以直接传一个箭头函数
mounted() {
    this.$refs.myheader.$on("handleEnterTodo", this.handleEnterTodo)
}

上面是第二种自定义事件的绑定方法,如果想要事件只调用一次就失效可以使用$once()传参和$on()一样。

  • 事件的解绑

    当不在用到这个事件时,可以将事件与组件解绑

    // 第一种方法,单事件解绑
    this.$refs.myheader.$off('handleEnterTodo')
    // 第二种方法,多事件解绑
    this.$refs.myheader.$off(['handleEnterTodo2',''handleEnterTodo2''])
    // 也可以不穿off里的参数,那就是把所有myheader组件的事件都解绑
    

由于事件是绑定在组件上为了子组件与父组件传值,那么当父组件销毁后,这里的自定义组件也就都自动销毁了。

事件总线

前面提到的props和自定义事件两个都只适用于父子组件之间传值,那如果我需要在兄弟之间或者爷孙组件之间传值呢?这里就引出了事件总线,与自定义事件的方法差不多,只不过是在vm创建的时候也就是beforeCreate()中给vue实例对象上添加一个“傀儡的组件”,这个“傀儡组件”需要有$on(),$once(),$off()这三个方法,来创建和销毁自定义事件,同时这个“傀儡组件”也可以被所有的组件访问到,那就是说所有组件都可以操作。

new Vue({
    el: "#app",
    render: h => h(App),
    beforeCreate() {
        // 安装全局事件总线
        Vue.prototype.$bus = this
    }
})

安装完全局事件总线之后就可以实现任意组件之间传递数据了。

  • 创建事件绑定

    事件一般都在组件挂载完之后加也就是在mounted()生命周期中,这里$bus是在vm中安装事件总线时定义的一个名字。

    mounted() {
        this.$bus.$on('handleDeleteTodo', this.handleDeleteTodo)
    // 这里可以把回调参数写成箭头函数
    	this.$bus.$on('handleDeleteTodo2', ()=>{})
    },
    
  • 触发事件

    当A组件在事件总线上创建一个事件后,B组件就可以触发事件将数据传递给A组件。

    this.$bus.$emit('handleCheckTodo', id)
    
  • 销毁事件

    由于事件现在都在事件总线上绑定了,一方面需要注意事件的名称不能重复,另一方面考虑到如果绑定事件过多可能会影响效率。所以当一个在销毁之前,应该将自己创建的事件给取消绑定,这里在生命周期钩子beforeDestory()中。

    beforeDestroy() {
        this.$bus.$off('handleCheckTodo')
    }
    

这样事件总线就实现了任意组件之间的通信。

消息订阅与发布(pubsub-js)

消息订阅与发布与事件总线差不多都是实现任意组件之间的通信,这里是通过第三方js库来实现的,npm安装pubsub-js库,这个库实现了消息订阅与发布的完整功能,只需要导入调用即可。

// 导入直接通过import就可,导入之后得到一个对象
import pubsub from 'pubsub-js'

// 创建消息订阅,调用subscribe方法
// 有两个参数,第一个是消息名称,第二个回调函数,返回值为该消息订阅的id
// 消息名称用来发布消息是用的
// 回调函数是消息发布之后的逻辑实现,会有两个参数传入
// 第一个是消息名称(固定),第二个是后续传入的参数(也就是消息发布携带的数据)
this.pubId = pubsub.subscribe('handleDeleteTodo', (msgName,obj)=>{
    this.handleDeleteTodo(obj)
})

// 消息发布,调用publish方法
// 参数第一个是消息名称,第二个即携带的数据
// 可以将携带的数据设定为对象,这样数据传递方便
pubsub.publish('handleDeleteTodo',obj)

// 消息的退订,调用unsubscribe方法
// 方法需要传入消息订阅的id
pubsub.unsubscribe(this.pubId)
posted @   hhhhuaz  阅读(89)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示