当做到比较大的项目时,一个vue文件里面可能要包含上千行代码,这样不利于后期维护与问题定位,抽离成组件就尤其重要了,重中之重的就是组件之间数据传递的几种方式。

父子传参、子父传参、兄弟传参、事件总线/事件车(EventBus)、Vuex。

父子传参

父元素将值通过自定义属性传递给子元素,子元素通过props:['属性名']接收父元素传递过来的值。

父组件

<template>
    <div class="wrap">
        <div>我是Father组件</div>
        <Son
          str="我是字符串"
          :num=5
          :obj="{cont:'我是一个对象'}"
          :func="()=>{this.hello()}"
          :arr="arr"></Son>
    </div>
</template>
<script>
    import Son from './Son'
    export default {
        name: "Father",
        components:{  Son  },
        data(){
            return{
                arr:[1,2,3]
            }
        },
        methods:{
            hello(){
                console.log("hello world!")
            }
        },
      
    }
</script>

子组件

<template>
    <div>我是Son组件</div>
</template>
<script>
    export default {
        name: "Son",
        props:{//props列表
            arr:Array,//定义参数类型
            num:Number,
            str:String,
            str2:{
                type:String,
                default:"我是默认字符串"//定义参数默认值
            },
            func:{
                type:Function,
                required:false//定义参数是否必须值
            },
            obj:{
                type: Object,
                required:false
            }
        },
        created() {
            console.log(this.str);//我是字符串
            console.log(this.str2);//我是默认字符串
            console.log(this.num);//5
            console.log(this.func);//hello(){console.log("hello world!");}
            console.log(this.arr);//[1,2,3]
            console.log(this.obj);//{cont:'我是一个对象'}
            this.func();//hello world!
        }
    }
</script>

子父传参

Vue的设计是单向数据流,理论上只允许父组件向子组件传递数据。想通过子组件向父组件传参必须是父元素传递一个事件给子组件,子组件通过父组件传递的方法进行传参,主要使用$emit('事件名',参数)。

//父组件App.vue
<template>
    <div>
        //在子组件绑定一个自定义事件名但是不能与系统事件重名
        <son @goSon="receiveMsg"/>
        <span>收到子组件传递的参数:{{num}}</span>
    </div>
</template>
<script>
import MyCom from './components/MyCom.vue'
  export default {
  components: { Son },
    data(){
        return{
            num:'' //用于接收子元素传递的信息
        }
    },
    methods: {
      receiveMsg(msg) {
        console.log('子传入的信息:', msg);
        this.num =msg
      }
    },
}
</script>

子组件:Son.vue

<template>
    <div>
        <button @click="chooseNum(1)">传参</button>
    </div>
</template>
<script>
     export default {
        methods: {
          chooseNum(num){
            //1.父元素需要传递一个方法给子元素
            //父元素: @suibian="receiveMsg"
            //2.子元素利用父元素传递的方法 来传递信息给父元素
            // 使用 $emit 来触发自定义事件
            // 参数1: 事件名
            // 参数2: 传递给事件中绑定方法的 参数
            this.$emit('receiveMsg', num)
         }
        },
    }
</script>

兄弟传参

兄弟传参依赖于父组件,兄弟A将参数传递给父,父将参数传递给兄弟B。

创建子组件1 FirstCom.vue

<template>
  <div class="first-com"> 
    <h1>第一个组件</h1>
    <input type="text" v-model="num">
    <button @click="doSure">传递</button>
  </div>
</template>
 
<script>
  export default {
    data() {
      return {
        num: ''
      }
    },
    methods: {
      doSure() {
        //简单方式--不推荐
        // this.$parent.first_kw = this.num
        // 优雅方式 -- 通过父传递的方法进行传参
        this.$emit('sendMsg',this.kw)
      }
    },
  }
</script>
 
<style lang="scss" scoped>
.first-com{
  background-color: green;
  min-height:200px
}
</style>

创建子组件2 SecondCom.vue

<template>
  <div class="first-com"> 
    <h1>第一个组件</h1>
    <p>来自父元素的传参:{{kw}}</p>
  </div>
</template>
 
<script>
  export default {
    props:['kw']
  }
</script>
 
<style lang="scss" scoped>
.first-com{
  background-color: blue;
  min-height:200px
}
</style>

父组件为App.vue

<template>
  <div>
    <p>来自子元素firstCom的值:{{first_kw}}</p>
    <first-com @sendMsg="receiveMsg"/>
    <second-com :kw="first_kw"/>
  </div>
</template>
 
<script>
import FirstCom from './components/FirstCom.vue'
import SecondCom from './components/SecondCom.vue'
  export default {
  components: { FirstCom, SecondCom },
    data() {
      return {
        first_kw: '', //来自子元素firstCom的值
      }
    },
    // 优雅的 子传父 : 1.父声明方法传递给子 2.子调用父传入的方法
    methods: {
      receiveMsg(msg) {
        this.first_kw = msg
      }
    },
  }
</script>

事件总线/事件车

在需要传递参数的组件中通过$root.$emit('自定义的名称',要传递的参数)来向外发布消息。

<template>
  <div>
    <button @click="chooseNum(1)">One</button>
    <button @click="chooseNum(2)">Two</button>
    <button @click="chooseNum(3)">Three</button>
  </div>
</template>
 
<script>
  export default {
    methods: {
      chooseNum(num) {
        console.log('this:', this);
        // this.$root: 是所有组件的根
        // 在 $root 中向外发布消息, 就可以被所有的组件接收到
        // 参数1: 自定义的值, 随便写
        // 参数2: 与参数1名称绑定的相关信息
        this.$root.$emit('哈哈', {msg:"消息内容", num})
      }
    },
  }
</script>

需要接收参数的组件通过$root.$on('发送消息组件自定义的名称',msg=>{在这处理接收到的消息})监听消息,接收完毕后在组件销毁前beforeDestroy中关闭监听$root.$off('自定义名称')。

<template>
  <div>
    <!-- 事件车: Event Bus -->
    <!-- 可以实现: 任意关系的多个组件间的消息传递 -->
    <my-com-bus/>
  </div>
</template>
 
<script>
import MyComBus from './components/MyComBus.vue'
  export default {
  components: { MyComBus },
    mounted () {
      // 监听 根元素 $root 中发送的消息
      // 参数1: 要听的消息名
      this.$root.$on('哈哈', function (data) {
        // data: 就是 $emit('哈哈', 具体数据data)
        console.log('收到的具体信息:', data);
      })
    },
    beforeDestroy () {
      // 通常: 在销毁前会 关闭监听
      this.$root.$off('哈哈')
    },
  }
</script>