vue组件之间的数据传递方法

3,032 阅读5分钟

1.props属性:

在父组件中,可以通过子组件标签属性的形式将数据或者函数传给子组件,子组件通过props去读取父组件传过来的数据

  • 用法

    • 父组件传数据给子组件:

      • 一般的属性值都是用来给子组件展示的

    • 子组件传数据给父组件

      • 属性值为函数类型的,一般是用来子组件向父组件传递数据,子组件通过调用父组件传过来的函数,可以修改父组件的状态数据

  • 缺点:

    • 隔层组件间传递: 必须逐层传递(麻烦)

    • 兄弟组件间: 必须借助父组件(麻烦)

  • 注意:

//子组件获取父组件传过来的值
props: {
    obj: {//obj为{id:'2'}
       type: Object
     }
}

引用类型的props,我们可以在子组件中直接修改引用类型属性的值(如:this.obj.id='3',会生效),但是不能直接改变引用类型存储的地址值(如:this.obj = {id: '3'}),会发出警告。

虽然子组件可以直接修改父组件的状态值,但我们不建议这样做,我们希望所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

脏数据问题:如v-model是实现了双向绑定,通过v-model更改数据,会更改父组件的数据,导致脏数据

解决方法:

利用计算属性的set和get,在set中需实现子组件调用父组件的方法,确保数据流向是单向的

computed:{
    msg: {
        get(){
            return this.data
        },
        set(){
            this.$emit('EventName', this.data)
        }    
}}

2.vue自定义事件:

  • 方式1: 给子组件标签绑定事件监听

    • 子组件向父组件的通信方式

      • 功能类似于function props

      • 通过在父组件中给子组件标签绑定自定义事件的监听,再由子组件触发事件,实现子组件向父组件传递数据的方法,事件名必须一致,且不能有大写字母,v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的)

        // 方式一: 通过v-on绑定
        <component @delete_todo="deleteTodo"/>
        // 方式二: 通过$on()绑定
        this.$refs.xxx.$on('delete_todo', function (todo) {
        this.deleteTodo(todo)})​
        通过this.$emit('delete_todo', todo)触发事件
    • 不适合隔层组件和兄弟组件间的通信

    • 注意:

      • 在组件标签上绑定事件,会被解析为vue自定义事件

      • 在普通的标签上绑定事件,会被解析为DOM事件

        <--!     
        表示在组件component绑定了一个名为click的vue自定义事件,
        相当于做了vm.$on('click',fn)    
        注意:不是常规理解的点击事件,这里与点击无关,只是名字刚好是click,
        当然实际操作中不建议取这类特殊的名字 
        -->
        <component @click="fn"/>
        <--! 表示在div上绑定一个click的点击事件,是DOM事件 -->​
        ​<div @click="fn"/>

  • 方式2: 通过单独的vm对象绑定监听/分发事件

    • 任意组件间通信(类似于pubsub)

      • 创建一个公用的vm对象

      import Vue from 'vue'export default new Vue()
      • 在接收消息的组件,绑定监听

      import vm from './vm.js'mounted(){
          vm.$on('delete_todo', function (todo) {
              this.deleteTodo(todo)
          })}
      • 在发送消息的组件,触发事件

      vm.$emit('delete_todo', todo)
    • 事件总线(EventBus),其实跟上面的用法一样,这里是把vm实例绑在了Vue的显示原型上

      //点击任意一个组件,其他组件的值跟着变化,利用总线去实现兄弟之间的通信
      Vue.prototype.bus = new Vue()
      var vm = new Vue({
          el:"#app"
          })    
      //v-a组件
      Vue.component('v-a', {
          template:'<div @click="handle">{{msg}}</div>',
          data(){
              return {
                  msg: 'v-a'
              }
          },
          mounted () {
              //绑定事件监听
              this.bus.$on('bus',(msg)=>{
                  this.msg = msg
              })
          },
          methods: {
              handle () {
                  //触发事件
                  this.bus.$emit('bus', this.msg)
              }
          }})
      //v-b组件
      Vue.component('v-b', {
          template:'<div @click="handle">{{msg}}</div>',
          data(){
              return {
                  msg: 'v-b' 
             }
          },
          mounted () {
              //绑定事件监听
              this.bus.$on('bus',(msg)=>{
                  this.msg = msg
              })
          }, 
         methods: {
              handle () {
                  //触发事件 
                 this.bus.$emit('bus', this.msg)
              }
          }})

3.消息的订阅和发布(pubsub)

  • 适用于任何关系的组件间的通信

  • 缺点:相对于vuex,管理不够集中

  • 用法:

    • 引入pubsub-js库

    • 在接收消息的组件订阅消息(subscribe)

      PubSub.subscribe('name',(name,data)=>{})
    • 在发送消息的组件发布消息 ( publish)

      PubSub.publish('name',data)
  • 其他用法可以参考官方API文档,这里不细说

4.vuex

vuex是专门为vue.js应用程序定义的状态管理模式,当多个组件需要共享数据时,我们一般用vuex去管理状态数据,实现多个组件间的相互通信


vuex的核心

  • state:存放数据

  • mutations:修改数据

  • actions:提交mutation,修改数据

  • getters:存放数据,类似于vue的计算属性

知道了这几个核心概念后,我们还需要知道怎么合理使用他们

  • 如何从仓库store读取数据

    • state

      从store的实例中我们可以通过store.state去获得状态数据,为了使得store可以全局使用,我们需要通过Vue.use(Vuex),将vuex注给所有vue子的组件,Vue.use回去调用插件的install方法,将stroe绑到Vue.prototype,这样Vue的实例对象都能通过vm.store获取或操作仓库的数据组件内通过this.$store.state可以获得

      state:{    
       data: 0
      }

    • getters

      有些状态熟悉需要根据其他状态属性计算而动态获得,一般都是存在getters中,组件通过调用this.$store.getters

      Getter 接受 state 作为其第一个参数

      getters:{
          data2(state){
              return state.data+1
          }
      }

  • 如何修改仓库store的数据

    • mutations(同步修改)

      更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,接受 state 作为第一个参数 ,mutation接收提交载荷,第二个参数为载荷,在大多数情况下,载荷应该是一个对象 ,这样可以接收多个数据

      mutations:{
          mutationType:(state,data)=>{
              state.data = data
          }
      }
    • actions(异步修改)

      在action中提交mutation,组件通过store.dispatch触发action

      因为vue的调试工具无法检测到mutations的异步变化,所以我们如果需要异步修改状态数据,一般是放在action去异步获取到数据后再提交mutation,确保vue的调试工具可以检测到,mutation的变化

  • 注意点

    当组件需要给vue中的状态数据添加属性时,通过obj.的形式无法实现

    因为Vuex 的 store 中的状态是响应式的,那么当我们变更状态时,监视状态的 Vue 组件也会自动更新。这也意味着 Vuex 中的 mutation 也需要与使用 Vue 一样遵守一些注意事项:

    1. 我们最好提前在 store 中初始化好所有所需属性。

    2. 当需要在对象上添加新属性时,我们应该

    • 使用 Vue.set(obj, 'newProp', 123), 或者

    • 以新对象替换老对象。例如,利用 stage-3 的对象展开运算符,我们可以这样写:

    state.obj = { ...state.obj, newProp: 123 }
  • 优化

    • import {mapState,mapGetters,mapMutations,mapActions} from "vuex";使得在使用vuex时,组件可以写的更加的简洁

    • 数据比较多的时候,使用module

    • 使用常量存放mutation_type

优点:

  • 多组件共享状态(数据的管理)

  • 组件间的关系也没有限制

  • 功能比pubsub强大, 更适用于vue项目

5.slot插槽

  • 父向子通信

  • 通信是带数据的标签

  • 注意: 标签是在父组件中解析