适合悄咪咪看的vue组件间通信常用方式,附源码!

vue组件间通信常用的六种方式
 组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。一般来说,组件可以有以下几种关系:适合悄咪咪看的vue组件间通信常用方式,附源码!_第1张图片
 如上图所示,A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)。
 针对不同的使用场景,如何选择行之有效的通信方式?
 下面就介绍几种vue组件之间通信的方式:

方法一 props/$emit

 这种方式比较简单,也是最基本的传值方式。
 父组件ParentView.vue中的currentTime通过props传入Children1.vue组件中显示,子组件通过触发父组件传递的方式getChildData,利用参数的形式将要传递给父组件的数据传递。
 父组件代码:

<template>
  <div class="parent_view">
    <div class="item_view">
      <h1>父组件</h1>
      <div style="text-align:left;">当前时间为:</div>
      <div class="time_view">{
     {
     currentTime}}</div>
    </div>
    <div class="item_view">
      <h1>子组件1</h1>
      <child1 :cTime="currentTime" @getChildData="getChildData"></child1>
    </div>
  </div>
</template>

<script>
import child1 from './children/Children1.vue'
export default {
     
  name: 'parentView',
  data () {
     
    return {
     
      currentTime: '',
    }
  },
  components: {
     
    child1,
  },
  methods: {
     
    getChildData (data) {
     
      this.currentTime = data;//将子组件传递的数据显示在父组件后面
    },
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.parent_view {
     
  width: 90%;
  height: 750px;
  margin: 0 auto;
  border: 1px solid cadetblue;
  border-radius: 5px;
  display: flex;
  justify-content: center;
  align-items: center;
}
.item_view {
     
  flex: 1;
  margin: 10px 10px;
  height: 95%;
  border: 1px solid cyan;
  border-radius: 5px;
  overflow: auto;
}
.time_view {
     
  color: cornflowerblue;
  font-weight: 800;
  text-align: center;
  margin: 10px;
}
</style>

 子组件代码:

<template>
  <div class="child_1">
    <p style="color:skyblue;">{
     {
     cTime}}</p>
    <button @click="PushData">向父组件传递子组件新时间</button>
    <p style="color:red;">{
     {
     time}}</p>
  </div>
</template>

<script>
export default {
     
  name: 'child_1',
  props: {
     
    cTime: {
     
      type: String,
      default: ''
    }
  },
  data () {
     
    return {
     
      time: new Date().toLocaleString()
    }
  },
  methods: {
     
    PushData () {
     
      let date = new Date().toLocaleString().replace(/\//g, '_')
      this.$emit('getChildData', date)
    }
  }
}
</script>

<style scoped>
</style>

tips: 子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。

方法二 $emit / $on

 这种方法也是比较轻量常见的方式,通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。
 注册全局bus

import Vue from 'vue'
const bus = new Vue()
export default bus

 父组件代码:

<template>
  <div class="parent_view">
    <div class="item_view">
      <h1>父组件</h1>
      <div style="text-align:left;">当前时间为:</div>
      <div class="time_view">{
     {
     currentTime}}</div>
      <button @click="send">向子组件发送时间</button>
    </div>
    <div class="item_view">
      <h1>子组件1</h1>
      <child1 :cTime="currentTime" @getChildData="getChildData"></child1>
    </div>
    <div class="item_view">
      <child2></child2>
    </div>
  </div>
</template>

<script>
import child1 from './children/Children1.vue'
import child2 from './children/Children2.vue'
import bus from '../utils/bus.js'
export default {
     
  name: 'parentView',
  data () {
     
    return {
     
      currentTime: '',
    }
  },
  components: {
     
    child1,
    child2,
  },
  methods: {
     
    getChildData (data) {
     
      this.currentTime = data;//将子组件传递的数据显示在父组件后面
    },
    send () {
     
      bus.$emit("sendChild", this.currentTime);
    },
  }
}
</script>

 子组件代码:

<template>
  <div class="child_1">
    <h1>子组件2</h1>
    <p>接收来自父组件的数据</p>
    <p style="color:red;">{
     {
     time}}</p>
    <childItem1></childItem1>
  </div>
</template>

<script>
import bus from '../../utils/bus.js'
import childItem1 from './items/ChildrenItems.vue'
export default {
     
  name: 'child_2',
  data () {
     
    return {
     
      time: ''
    }
  },
  components: {
     
    childItem1
  },
  methods: {
     

  },
  mounted () {
     
    bus.$on("sendChild", data => {
     
      this.time = data;
    });
  }
}
</script>

<style scoped>
</style>

 子组件的子组件代码:

<template>
  <div class="child_1">
    <h3>子组件2_2</h3>
    <p>接收来自父组件的数据</p>
    <p style="color:cyan;">{
     {
     time}}</p>
  </div>
</template>

<script>
import bus from '../../../utils/bus.js'
export default {
     
  name: 'child_2',
  data () {
     
    return {
     
      time: ''
    }
  },

  methods: {
     

  },
  mounted () {
     
    bus.$on("sendChild", data => {
     
      this.time = data;
    });
  }
}
</script>

<style scoped>
</style>

方法三:v-modal

 v-modal实现的是仅限父子组件的数据双向绑定。

 父组件代码:

<template>
  <div class="parent_view">
    <div class="item_view">
      <h1>父组件</h1>
      <p style="text-align:left;">message</p>
      <p style="text-align:left;color:red;">{
     {
     massage}}</p>
    </div>
    <div class="item_view">
      <child3 v-model="massage"></child3>
    </div>
  </div>
</template>

<script>
import child3 from './children/Children3.vue'
export default {
     
  name: 'parentView',
  data () {
     
    return {
     
      currentTime: '',
      massage: '这是简单的组件传值的例子!',
    }
  },
  components: {
     
    child3,
  },
  methods: {
     
  }
}
</script>

 子组件代码:

<template>
  <div class="child_1">
    <h1>子组件3</h1>
    <p>接收来自父组件的message</p>
    <p style="color:green;">{
     {
     pMessage}}</p>
    <button @click="change">改变modal的值</button>
  </div>
</template>

<script>
export default {
     
  name: 'child_3',
  data () {
     
    return {
     

    }
  },
  model: {
     
    prop: 'pMessage',
    event: 'setValue'
  },
  props: {
     
    pMessage: {
     
      type: String,
      default: ''
    }
  },
  methods: {
     
    change () {
     
      this.$emit('setValue', '哇咔咔~~~~')
    }
  },
}
</script>

<style scoped>
</style>

方法四:$parent / $children / $refs

 ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
 $parent / $children:访问父 / 子实例
 需要注意的是:这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。

 不过,这两种方法的弊端是,无法在跨级或兄弟间通信

 如果子组件是公共组件,会被多个父组件调用,那么$parent会怎么获取?改变他们的属性将会怎么变化?父组件中没有这个属性怎么办?

 针对不同父组件调用,子组件会每次都会生成一个实例,这也是Vue的重要机制。parent会获取每个调用它的父组件实例。子组件中通过parent会获取每个调用它的父组件实例。子组件中通过parent会改变每个调用它的父组件中的对应属性。

 父组件代码:

<template>
  <div class="parent_view">
    <div class="item_view">
      <h1>父组件</h1>
      <p style="text-align:left;">sangName</p>
      <p style="text-align:left;color:plum;">{
     {
     sangName}}</p>
    </div>

    <div class="item_view">
      <child4 :sangName="sangName"></child4>
    </div>
  </div>
</template>

<script>
import child4 from './children/Children4.vue'
export default {
     
  name: 'parentView',
  data () {
     
    return {
     
      sangName: '现在听的歌叫《Please Don’t Touch》',
    }
  },
  components: {
     
    child4,
  },
  methods: {
     
    changeSang (data) {
     
      this.sangName += data
    },
    sendSang () {
     
      this.$children[0].childChange(this.$children[0].childData)
    }
  }
}
</script>

 子组件代码:

<template>
  <div class="child_1">
    <h1>子组件4</h1>
    <p>接收来自父组件的sangName</p>
    <p>{
     {
     $attrs.sangName}}</p>
    <button @click="sendSang">改变sangName的值</button>
  </div>
</template>

<script>
export default {
     
  name: 'child_4',
  data () {
     
    return {
     
      childData: '囍'
    }
  },
  props: {
     
    data: {
     
      type: String,
      default: ''
    }
  },
  methods: {
     
    childChange (data) {
     
      this.childData += data
    },
    sendSang () {
     
      this.$parent.changeSang(this.childData)
    }
  },
}
</script>

<style scoped>
</style>

方法五:$attrs / $listeners

 多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法----attrs/attrs/listeners

attrs:包含了父作用域中不被prop所识别(且获取)的特性绑定(class和style除外)。当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(class和style除外),并且可以通过v−bind="attrs:包含了父作用域中不被prop所识别(且获取)的特性绑定(class和style除外)。当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(class和style除外),并且可以通过v−bind=“attrs” 传入内部组件。通常配合 interitAttrs 选项一起使用。
listeners:包含了父作用域中的(不含.native修饰器的)v−on事件监听器。它可以通过v−on="listeners:包含了父作用域中的(不含.native修饰器的)v−on事件监听器。它可以通过v−on=“listeners” 传入内部组件

 父组件代码:

<template>
  <div class="parent_view">
    <div class="item_view">
      <h1>父组件</h1>
    </div>

    <div class="item_view">
      <child5
        :auther="auther"
        :language="language"
        :skill="skill"
        :habit="habit"
        title="前端攻城狮~w(゚Д゚)w"
      ></child5>
    </div>
  </div>
</template>

<script>
import child5 from './children/Children5.vue'
export default {
     
  name: 'parentView',
  data () {
     
    return {
     
      auther: 'lookingForw_4585',
      language: 'javascript',
      skill: 'css / html5',
      habit: 'Music'
    }
  },
  components: {
     
    child5,
  },

}
</script>

 子组件代码:

<template>
  <div class="child_1">
    <h1>子组件5</h1>
    <p>接收来自父组件的auther</p>
    <p style="color:#1ee459;">{
     {
     auther}}</p>
    <p>接收来自父组件的$attrs</p>
    <p>{
     {
     $attrs}}</p>
    <hr />
    <childItem1 v-bind="$attrs"></childItem1>
  </div>
</template>

<script>
import childItem1 from './items/ChildrenItems2'
export default {
     
  name: 'child_5',
  data () {
     
    return {
     
    }
  },
  components: {
     
    childItem1
  },
  inheritAttrs: false, // 以关闭自动挂载到组件根元素上的没有在props声明的属性
  props: {
     
    auther: {
      // auther作为props属性绑定
      type: String,
      default: ''
    }
  },
  methods: {
     
  },
}
</script>

<style scoped>
.child_1 {
     
  overflow-x: auto;
}
</style>

 儿子组件:

<template>
  <div class="child_1">
    <h3>子组件5_1</h3>
    <p>接收来自父组件的language</p>
    <p style="background-color:#999999;">{
     {
     language}}</p>
    <p>子组件5_1的$attrs:</p>
    <p>{
     {
     $attrs}}</p>
    <hr />
    <childItem3 v-bind="$attrs"></childItem3>
  </div>
</template>

<script>
import childItem3 from './ChildrenItems3'
export default {
     
  name: 'child_5_1',
  data () {
     
    return {
     
    }
  },
  components: {
     
    childItem3
  },
  inheritAttrs: false,
  props: {
     
    language: {
     
      type: String,
      default: ''
    }
  },
  methods: {
     

  },
  mounted () {
     
  }
}
</script>

<style scoped>
</style>

 孙子组件:

<template>
  <div class="child_1">
    <h3>子组件5_1_1</h3>
    <p style="color:red;">{
     {
     title}}</p>
    <p>接收来自父组件的$attrs</p>
    <p style="color:cyan;">{
     {
     $attrs}}</p>
  </div>
</template>

<script>
export default {
     
  name: 'child_5_1_1',
  data () {
     
    return {
     
    }
  },
  props: {
     
    skill: {
     
      type: String,
      default: ''
    },
    title: {
     
      type: String,
      default: ''
    }
  },
  methods: {
     

  },
  mounted () {
     
  }
}
</script>

<style scoped>
</style>

效果如图:
适合悄咪咪看的vue组件间通信常用方式,附源码!_第2张图片
 如上图所示attrs表示没有继承数据的对象,格式为属性名:属性值。Vue2.4提供了attrs表示没有继承数据的对象,格式为属性名:属性值。
 Vue2.4提供了attrs , listeners来传递数据与事件,跨级组件之间的通讯变得更简单。简单来说:listeners来传递数据与事件,跨级组件之间的通讯变得更简单。简单来说:attrs与listeners是两个对象,listeners是两个对象,attrs 里存放的是父组件中绑定的非 Props 属性,$listeners里存放的是父组件中绑定的非原生事件。

方法六 vuex

适合悄咪咪看的vue组件间通信常用方式,附源码!_第3张图片
 vuex是vue官方提供的状态管理工具,当项目较复杂,部分数据在多个组件中使用时,使用vuex是首选选择。具体的用法就不在赘述,网络上有很多教程和文章介绍。

 最后附上上面gif图片的父组件代码。

<template>
  <div class="parent_view">
    <div class="item_view">
      <h1>父组件</h1>
      <div style="text-align:left;">当前时间为:</div>
      <div class="time_view">{
     {
     currentTime}}</div>
      <button @click="send">向子组件发送时间</button>
      <p style="text-align:left;">message</p>
      <p style="text-align:left;color:red;">{
     {
     massage}}</p>
      <p style="text-align:left;">sangName</p>
      <p style="text-align:left;color:plum;">{
     {
     sangName}}</p>
    </div>
    <div class="item_view">
      <h1>子组件1</h1>
      <child1 :cTime="currentTime" @getChildData="getChildData"></child1>
    </div>
    <div class="item_view">
      <child2></child2>
    </div>
    <div class="item_view">
      <child3 v-model="massage"></child3>
    </div>
    <div class="item_view">
      <child4 :sangName="sangName"></child4>
    </div>
    <div class="item_view">
      <child5
        :auther="auther"
        :language="language"
        :skill="skill"
        :habit="habit"
        title="前端攻城狮~w(゚Д゚)w"
      ></child5>
    </div>
  </div>
</template>

<script>
import child1 from './children/Children1.vue'
import child2 from './children/Children2.vue'
import child3 from './children/Children3.vue'
import child4 from './children/Children4.vue'
import child5 from './children/Children5.vue'
import bus from '../utils/bus.js'
export default {
     
  name: 'parentView',
  data () {
     
    return {
     
      currentTime: '',
      massage: '这是简单的组件传值的例子!',
      sangName: '现在听的歌叫《Please Don’t Touch》',
      auther: 'lookingForw_4585',
      language: 'javascript',
      skill: 'css / html5',
      habit: 'Music'
    }
  },
  components: {
     
    child1,
    child2,
    child3,
    child4,
    child5,
  },
  methods: {
     
    getChildData (data) {
     
      this.currentTime = data;//将子组件传递的数据显示在父组件后面
    },
    send () {
     
      bus.$emit("sendChild", this.currentTime);
    },
    changeSang (data) {
     
      this.sangName += data
    },
    sendSang () {
     
      this.$children[3].childChange(this.$children[3].childData)
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.parent_view {
     
  width: 90%;
  height: 750px;
  margin: 0 auto;
  border: 1px solid cadetblue;
  border-radius: 5px;
  display: flex;
  justify-content: center;
  align-items: center;
}
.item_view {
     
  flex: 1;
  margin: 10px 10px;
  height: 95%;
  border: 1px solid cyan;
  border-radius: 5px;
  overflow: auto;
}
.time_view {
     
  color: cornflowerblue;
  font-weight: 800;
  text-align: center;
  margin: 10px;
}
</style>

  tips:

     如果本文对您有帮助,欢迎评论告知。

你可能感兴趣的:(Vue,组件通信,vue,js,javascript)