Vue中常用的组件通信方式

180 阅读4分钟

Vue2是一款流行的JavaScript框架,其组件系统允许开发人员将复杂的用户界面拆分成小的、可重用的组件。在这篇文章中,我将重点介绍Vue2组件通信的各种方法。

父子组件通信

Vue2中,父组件可以向子组件传递数据和事件。通过props选项,父组件可以将数据传递给子组件。子组件可以使用这些props来渲染自身的模板或执行其他操作。

<template>
  <child-component :message="parentMessage"></child-component>
</template>

<script>
export default {
  data() {
    return {
      parentMessage: 'Hello from parent'
    }
  }
}
</script>

// 子组件
<template>
  <div>{{ message }}</div>
</template>

<script>
export default {
  props: ['message']
}
</script>

此外,子组件可以向父组件发送事件,以便父组件可以对这些事件作出响应。子组件可以使用$emit方法来触发一个自定义事件。

<template>
  <button @click="sendData">Send Data</button>
</template>

<script>
export default {
  methods: {
    sendData() {
      this.$emit('dataSent', 'Some data')
    }
  }
}
</script>

// 父组件
<template>
  <child-component @dataSent="handleData"></child-component>
</template>

<script>
export default {
  methods: {
    handleData(data) {
      console.log(data) // 'Some data'
    }
  }
}
</script>

兄弟组件通信

在Vue2中,兄弟组件之间的通信可以通过共同的父组件来实现。具体来说,兄弟组件可以使用emit和on方法来发送和接收事件。当一个兄弟组件触发事件时,它会将事件发送给共同的父组件。父组件可以数据转发给其他兄弟组件。

第一种方法:

<template>
  <div>
    <sibling-component-a @dataSent="handleData"></sibling-component-a>
    <sibling-component-b :message="message"></sibling-component-b>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: ''
    }
  },
  methods: {
    handleData(data) {
      this.message = data
    }
  }
}
</script>

// 兄弟组件A
<template>
  <button @click="sendData">Send Data</button>
</template>

<script>
export default {
  methods: {
    sendData() {
      this.$emit('dataSent', 'Some data')
    }
  }
}
</script>

// 兄弟组件B
<template>
  <div>{{ message }}</div>
</template>
<script> 
export default { 
    props: ['message'] 
} 
</script>

第二种方法:

// main.js
Vue.prototype.$bus = new Vue()  // 共享bus

// 兄弟组件A 
<template>
  <button @click="sendData">Send Data</button> 
</template>

script> 
export default { 
  methods: { 
  sendData() {
    this.$bus.$emit('dataSent', 'Some data')
  } 
 } 
} 
</script>

// 兄弟组件B
<template> 
  <div>{{ message }}</div> 
</template> 
<script> 
export default { 
  data() {
    return {
      message: ''
    }
  },
  mounted(){
    this.$bus.$on('dataSent', (msg) => {
       this.message = msg
    })
  },
  beforeDistory(){
    this.$bus.$off('dataSent')
  }
} 
</script>

Vuex

Vuex是Vue.js的状态管理库,它提供了一个集中式的存储来管理应用程序的状态,让组件以一种可预测和组织良好的方式访问和更新状态。Vuex存储由几个组件组成:state、mutations、actions和getters。其中,state是存储应用程序状态的对象,mutations是用于更改状态的函数,actions是用于处理异步操作的函数,getters是用于从存储中获取状态的函数。通过使用Vuex,开发人员可以更轻松地管理应用程序的状态,并使组件之间的通信更加简单和清晰。

假设有一个简单的Vue.js应用程序,其中有一个计数器组件和一个显示计数器值的组件。这两个组件都需要访问和更新同一个计数器值,我们可以使用Vuex来管理该值的状态。

首先,在应用程序中安装Vuex,然后创建一个Vuex store。store由state、mutations、actions和getters组成。在这个例子中,我们只需要一个state和一个mutation。

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    counter: 0
  },
  mutations: {
    increment (state) {
      state.counter++
    }
  }
})

export default store

在上面的代码中,我们创建了一个名为store的新的Vuex存储,并定义了一个名为counter的状态属性和一个名为increment的mutation函数,用于将计数器值加1。

接下来,在计数器组件中,我们可以通过调用increment mutation来更新计数器值。

<template>
  <div>
    <p>计数器组件</p>
    <button @click="incrementCounter">增加计数器</button>
  </div>
</template>

<script>
export default {
  methods: {
    incrementCounter () {
      this.$store.commit('increment')
    }
  }
}
</script>

在上面的代码中,我们定义了一个名为incrementCounter的方法,该方法调用了increment mutation。

最后,在显示计数器值的组件中,我们可以使用getters来获取计数器的值。

<template>
  <div>
    <p>显示计数器值的组件</p>
    <p>计数器值为:{{ counter }}</p>
  </div>
</template>

<script>
export default {
  computed: {
    counter () {
      return this.$store.state.counter
    }
  }
}
</script>

在上面的代码中,我们定义了一个名为counter的计算属性,该属性使用$store.state.counter来获取计数器的值。

通过使用Vuex,我们可以轻松地管理应用程序的状态,并确保组件之间的通信更加简单和清晰。

provide和inject

在Vue2中,提供了一种高级的组件通信方式,即provide和inject。使用provide和inject可以让你在组件树中任何位置访问一个共享的对象。

在父组件中,你可以使用provide选项来提供一个对象,该对象可以被任何后代组件使用。在后代组件中,你可以使用inject选项来注入这个对象。

<template>
  <parent-component>
    <child-component></child-component>
  </parent-component>
</template>

<script>
export default {
  provide: {
    sharedData: 'Hello from ancestor'
  }
}
</script>

// 父组件
<template>
  <div>
    <slot></slot>
  </div>
</template>

// 子组件
<template>
  <div>{{ sharedData }}</div>
</template>

<script>
export default {
  inject: ['sharedData']
}
</script>

需要注意的是,provide和inject绑定并不是可响应的,如果想传递响应式的数据,可以这样写

// parent.vue
data () {
    tableData: {
        list: []
    }
},
provide () {
    return {
        tableData: this.tableData
    }
},
methods:{
    getTableData(){
      axios.get("url")
        .then((res) => {
          this.tableData.list = res.data.tableData;
          console.log("我是爷组件:", this.tableData.list);
        });
    },
  }

// child.vue
...
inject: ['taleData']
...
<el-table :data="tableData.list">

attrs和listeners

在Vue2中,每个组件实例都会有一些特殊的属性和方法,可以用来访问组件的属性、事件、插槽等信息。其中,attrs和listeners是两个非常有用的属性。

attrs是一个对象,包含了传递给组件的非prop属性。你可以在组件的模板中使用v-bind="$attrs"来将这些属性传递给子组件。

<template>
  <child-component message="Hello" data-foo="bar" @update="updateFn" v-on="$listeners"></child-component>
</template>

// 子组件
<template>
  <div>{{ message }}</div>
  <div>{{ $attrs }} // { data-foo: bar }
  <button @click="clickFn">click me</button>
</template>
<script>

export default {
  props: ['message'],
  methods: {
    clickFn(){
      console.log(this.$listeners) // { update: ƒ invoker() }
    }
  }
}
</script>


ref

在Vue2中,ref属性允许你访问组件的DOM元素或子组件实例。你可以通过在模板中使用ref属性来为组件或DOM元素指定一个引用名。然后,你可以通过$refs来访问这些引用。

<template>
  <child-component ref="child"></child-component>
</template>

// 子组件
<template>
  <div>Child Component</div>
</template>

<script>
export default {
  mounted() {
    console.log(this.$refs.child) // 子组件实例
  }
}
</script>

$parent

在Vue.js中,每个组件都有一个$parent属性,它指向该组件的父组件。可以通过$parent属性访问父组件的属性或方法,从而实现父子组件之间的通信。

例如,如果一个子组件需要在父组件中触发一个事件,可以使用parent.parent.emit()方法。假设父组件有一个名为“handleEvent”的方法:

// 父组件
<template>
  <div>
    <child-component></child-component>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue'

export default {
  methods: {
    handleEvent() {
      console.log('Event handled in parent component')
    }
  },
  components: {
    ChildComponent
  }
}
</script>

子组件可以通过$parent访问父组件的handleEvent方法,从而触发事件:

// 子组件
<template>
  <button @click="$parent.handleEvent()">Trigger Parent Event</button>
</template>

在Vue2中,parent属性允许你访问父组件实例。但是,建议尽量避免使用$parent,因为它会让你的组件与其它组件的层次结构紧密耦合,使组件变得不够灵活。

$children

在Vue.js中,$children是一个组件的直接子组件数组,是一个Vue实例的属性。可以通过访问这个属性来访问和操作组件的子组件。

需要注意的是,$children是一个动态属性,它的值会随着组件的渲染和销毁而改变。此外,使用children访问子组件时,需要注意子组件的顺序和数量,因为Vue.js不保证$children数组中子组件的顺序和组件树中的顺序相同。

下面是一个使用$children属性访问子组件的简单示例:

<template>
  <div>
    <button @click="onButtonClick">Click me</button>
    <child-component></child-component>
  </div>
</template>

<script>
  import ChildComponent from './ChildComponent.vue';

  export default {
    components: {
      ChildComponent
    },
    methods: {
      onButtonClick() {
        // 访问子组件
        const childComponent = this.$children[0];
        // 调用子组件的方法
        childComponent.doSomething();
      }
    }
  };
</script>

总结:

常见使用场景可以分为三类:

  • 父子组件通信: props/emit、parent/children、 provide/inject、ref、attrs/listeners
  • 兄弟组件通信: eventBus、 vuex
  • 跨级通信: eventBus、 vuex、 provide/inject、attrs/listeners