美文网首页
vue组件之间的通信方式(一)

vue组件之间的通信方式(一)

作者: moofyu | 来源:发表于2020-03-18 22:27 被阅读0次

    一般来说,组件可以有以下几种关系:

    如上图所示,A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)。

    1.props和$emit

    父组件:

    • 通过props向子组件传递数据;
    • 绑定v-on来监听子组件触发的事件, 并且可以在监听函数中依次取得所有从子组件传来的参数

    子组件:

    • 通过props得到相关的数据,
    • 通过$emit(event, [...参数])触发一个自定义的事件
    //子组件
    Vue.component('child',{
        data(){
          return {
            mymessage:this.message
          }
        },
        template:`
          <div>
            <input type="text" v-model="mymessage" @input="passData(mymessage)"> </div>
        `,
        props:['message'],//得到父组件传递过来的数据
        methods:{
          passData(val){
            //触发父组件中的事件
            this.$emit('getChildData',val)
          }
        }
      })
    //父组件
    Vue.component('parent',{
        template:`
          <div>
            <p>this is parent compoent!</p>
            <child :message="message" v-on:getChildData="getChildData"></child>
          </div>
        `,
        data(){
          return {
            message:'hello'
          }
        },
        methods:{
          //执行子组件触发的事件
          getChildData(val){
            console.log(val)
          }
        }
      })
      var app=new Vue({
        el:'#app',
        template:`
          <div>
            <parent></parent>
          </div>
        `
      })
    
    

    2. v-model

    • 父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,
    • 子组件中通过this.$emit(‘input',val)自动修改v-model绑定的值
    Vue.component('child',{
        props:{
          value:String, //v-model会自动传递一个字段为value的prop属性
        },
        data(){
          return {
            mymessage:this.value
          }
        },
        methods:{
          changeValue(){
            this.$emit('input',this.mymessage);//通过如此调用可以改变父组件上v-model绑定的值
          }
        },
        template:`
          <div>
            <input type="text" v-model="mymessage" @change="changeValue">
          </div>
      })
    Vue.component('parent',{
        template:`
          <div>
            <p>this is parent compoent!</p>
            <p>{{message}}</p>
            <child v-model="message"></child>
          </div>
        `,
        data(){
          return {
            message:'hello'
          }
        }
      })
      var app=new Vue({
        el:'#app',
        template:`
          <div>
            <parent></parent>
          </div>
        `
      })
    

    3. $attrs和$listeners

    • A组件传递数据给C组件
    • 用第一种方式可以,但A和C之间有更多组件的话就变得复杂
    • 用$attrs和$listeners来隔代传递数据
    Vue.component('C',{
        template:`
          <div>
            <input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)"> </div>
        `,
        methods:{
          passCData(val){
            //触发父组件A中的事件
            this.$emit('getCData',val)
          }
        }
      })//在此我向大家推荐一个前端全栈开发交流圈:619586920 突破技术瓶颈,提升思维能力
      Vue.component('B',{
        data(){
          return {
            mymessage:this.message
          }
        },
        template:`
          <div>
            <input type="text" v-model="mymessage" @input="passData(mymessage)">
            <!-- C组件中能直接触发getCData的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性 -->
            <!-- 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) -->
            <C v-bind="$attrs" v-on="$listeners"></C>
          </div>
        `,
        props:['message'],//得到父组件传递过来的数据
        methods:{
          passData(val){
            //触发父组件中的事件
            this.$emit('getChildData',val)
          }
        }
      })
      Vue.component('A',{
        template:`
          <div>
            <p>this is parent compoent!</p>
            <B :messagec="messagec" :message="message" v-on:getCData="getCData" v-on:getChildData="getChildData(message)"></B>
          </div>
        `,
        data(){
          return {
            message:'hello',
            messagec:'hello c' //传递给c组件的数据
          }
        },
        methods:{
          getChildData(val){
            console.log('这是来自B组件的数据')
          },
          //执行C子组件触发的事件
          getCData(val){
            console.log("这是来自C组件的数据:"+val)
          }
        }
      })
      var app=new Vue({
        el:'#app',
        template:`
          <div>
            <A></A>
          </div>
        `
      })
    

    总结:

    • 父组件绑定传给孙组件的属性,绑定事件来监听孙组件的触发事件
    • 子组件绑定 <C v-bind="$attrs" v-on="$listeners"></C>
    • 孙组件通过$attrs.messagec拿到父组件传递的数据,通过this.$emit('getCData',val)触发父组件

    4 中央事件总线(事件中心)

    • 实现父子、兄弟、跨级组件的通信

    • 使用一个空的 Vue 实例bus作为中央事件总线,通过bus.$emit触发事件,bus.$on监听触发的事件

    • 空的Vue实例 bus

    //bus.js
    import Vue from 'vue'
    
    const bus = new Vue()
    export default bus
    
    • 组件add
    <!--html结构-->
    <template>
      <div>
        <p>我是add组件的num:{{num}} </p>
        <button @click='add'>增加</button>
      </div>
    </template>
    
    // js
    <script>
    import bus from '../bus.js'
    export default {
      data() {
        return {
          num: ''
        }
      },
      methods: {
        add() {
          if (this.num) {
            this.num++
            bus.$emit('num-change', this.num)
          } else {
            this.num = 1
            bus.$emit('num-change', this.num)
          }
        }
      }
    }
    </script>
    
    • 组件sub
    <!--html结构-->
    <template>
      <div>
        <p>我是sub组件的num:{{num}}</p>
      </div>
    </template>
    
    
    // js
    <script>
    import bus from '../bus.js'
    export default {
      data() {
        return {
          num: ''
        }
      },
      created() {
        bus.$on('num-change', num => {
          this.num = num
        })
      }
    }
    </script>
    
    

    5. provide和inject

    • 祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量
    • 主要解决了跨级组件间的通信问题
    • 举个例子:假设有两个组件: A.vue 和 B.vue,B 是 A 的子组件
    //  A.vue祖先组件 提供foo
    //第一种
    export default {
      name: "grandfather",
      //建议,如果你需要传一个对象,那么第二种是传不了的
      provide(){
        return{
          foo:'子非鱼'
        }
      },
    }
    //第二种
    export default {
      name: "grandfather",
      provide:{
        foo:'子非鱼~~~~'
      },
    }
    
    // B.vue,后代组件 注入foo
    export default {
      inject:['foo'],
      mounted () {
        console.log(this.foo);  // 子非鱼
      }
    }
    

    可以把依赖注入看作一部分“大范围有效的 prop”, 祖先组件不需要知道哪些后代组件使用它提供的属性,后代组件不需要知道被注入的属性来自哪里。

    提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的----vue官方文档

    所以,上面 A.vue 的 foo如果改变了,B.vue 的 this.foo是不会改变的,仍然是子非鱼。

    当你像下面这样做的时候,就可以响应了

    provide(){
      return{
        test:this.activeData
      }
    },
    data() {
      return {
        activeData:{name:'子非鱼'},
      }
    }
    mounted(){
      setTimeout(()=>{
        this.activeData.name = 'cool';
      },3000)
    }
    

    这就是vue中写道的对象的属性是可以响应的

    然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。

    6. $parent 、$children与 ref

    • ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例;
    • $parent / $children:访问父 / 子实例;
    • 这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据;
    父子组件通信
    // 在父组件中给子组件赋值
    this.$refs.[子组件的ref].[子组件的属性]="zfy"’
    
    //给 child 组件内的 mymessage 属性赋值
    this.$children[0].mymessage = 'hello'; 
    
    // 给 parent 组件中的 message 赋值
    this.$parent.message= 'hello'; 
    

    看个用 ref来访问组件的例子:

    // component-a 子组件
    export default {
      data () {
        return {
          title: 'Vue.js'
        }
      },
      methods: {
        sayHello () {
          window.alert('Hello');
        }
      }
    }
    
    // 父组件
    <template>
      <component-a ref="comA"></component-a>
    </template>
    <script>
      export default {
        mounted () {
          const comA = this.$refs.comA;
          console.log(comA.title);  // Vue.js
          comA.sayHello();  // 弹窗
        }
      }
    </script>
    

    7. boradcast和dispatch

    vue1.0中提供了这种方式,但vue2.0中没有,但很多开源软件都自己封装了这种方式,比如min ui、element ui和iview等。

    8. vuex处理组件之间的数据交互

    如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候才有上面这一些方法可能不利于项目的维护,vuex的做法就是将这一些公共的数据抽离出来,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。
    详情可参考:https://vuex.vuejs.org/zh-cn/

    相关文章

      网友评论

          本文标题:vue组件之间的通信方式(一)

          本文链接:https://www.haomeiwen.com/subject/wethyhtx.html