父子组件通信#
props和$emit#
props接受父组件传递的数据$emit派发事件
<!-- CompA.vue -->
<template>
<div>
<h1>This is Comp A<h1>
<!-- 监听自定义事件,传入数据 -->
<CompB @count="getCount" :message="message"></CompB>
</div>
</template>
<script>
import CompB from './CompB'
export default {
components: {},
data() {
return {
message: 'hello'
}
},
methods: {
getCount(val) {
console.log(val)
}
}
}
</script>
<!-- CompB.vue -->
<template>
<div>
<h2>This is Comp B</h2>
<button @click="emitEvent">Emit Event</button>
</div>
</template>
<script>
export default {
// 获取父组件传入的数据
props: ['message'],
methods: {
emitEvent() {
this.$emit('count', 123) // 派发自定义事件
}
}
}
</script>使用场景:一般使用场景。
$parent和$children#
$parent获取父组件实例(数据和方法)$children获得子组件实例的数组(数据和方法)
<!-- CompA.vue -->
<template>
<div>
<h1>This is Comp A<h1>
<CompB></CompB>
</div>
</template>
<script>
import CompB from './CompB'
export default {
components: {},
data() {
return {
message: 'hello'
}
},
mounted() {
console.log(this.$children[0].count) // 获取子组件数据
this.$children[0].printCount() // 调用子组件的方法
}
methods: {
printMsg() {
console.log(this.message)
}
}
}
</script>
<!-- CompB.vue -->
<template>
<div>
<h2>This is Comp B</h2>
</div>
</template>
<script>
export default {
data() {
return {
count: 2018
}
}
mounted() {
console.log(this.$parent.message) // 获取父组件数据
this.$parent.printMsg() // 调用父组件的方法
},
methods: {
printCount() {
console.log(this.count)
}
}
}
</script>使用场景:需要灵活操作父组件或者子组件数组或者方法时。
v-model#
使用v-model可以在组件中实现双向绑定。
<!-- CompA.vue -->
<template>
<div>
<h1>This is Comp A<h1>
<div>{{content}}</div>
<CompB v-model="content"></CompB>
</div>
</template>
<script>
import CompB from './CompB'
export default {
components: {
CompB
},
data() {
return {
content: 'hello'
}
}
}
</script>
<!-- CompB.vue -->
<template>
<div>
<h2>This is Comp B</h2>
<div>{{content}}</div>
<input type="text" :value="content" @input="$emit('input', $event.target.value)"></input>
</div>
</template>
<script>
export default {
props: {
content: String
},
// 需要使用 model 指明绑定的对象和事件
model: {
prop: 'content',
event: 'input'
}
}
</script>使用场景:需要双向绑定组件的表单数据时。
.sync#
传递数据的时候,添加修饰符sync可以实现数据双向绑定。.sync是一个语法糖,相当于@update:propData="propData=$event"。
注意:使用.sync会出现警告信息,双向绑定的数据后期也不好维护,不建议使用。
<!-- CompA.vue -->
<template>
<div>
<h1>This is Comp A<h1>
<div>{{count}}</div>
<!-- <CompB :count="count" @update:count="count=$event"></CompB> -->
<CompB :count.sync="count"></CompB>
</div>
</template>
<script>
import CompB from './CompB'
export default {
components: {
CompB
},
data() {
return {
count: 1
}
}
}
</script>
<!-- CompB.vue -->
<template>
<div>
<h2>This is Comp B</h2>
<div>{{count}}</div>
<button @click="add">add count</button>
</div>
</template>
<script>
export default {
props: {
count: Number
}
methods: {
add() {
this.count += 1
}
},
watch: {
'count'(newVal) {
this.$emit('update:count', newVal) // 必须派发 update:propData 事件,并传入新的数据
}
}
}
</script>使用场景:需要双向绑定数据时。
ref#
ref可以注册绑定组件的,并且引用组件实例,因此可以很灵活的操作子组件的数据和方法。
<!-- CompA.vue -->
<template>
<div>
<h1>This is Comp A<h1>
<!-- 注册引用信息 -->
<CompB ref="compB"></CompB>
</div>
</template>
<script>
import CompB from './CompB'
export default {
components: {
CompB
},
mounted() {
console.log(this.$refs.compB.count) // 获取子组件数据
this.$refs.compB..printCount() // 调用子组件的方法
}
}
</script>
<!-- CompB.vue -->
<template>
<div>
<h2>This is Comp B</h2>
</div>
</template>
<script>
export default {
data() {
return {
count: 2018
}
},
methods: {
printCount() {
console.log(this.count)
}
}
}
</script>使用场景:需要灵活操作子组件数据和方法时。
slot-scope#
使用作用域插槽也可以使子组件和父组件进行通信。
<!-- CompA.vue -->
<template>
<div>
<h1>This is Comp A<h1>
<CompB :todos="todos">
<!-- 获得子组件插槽绑定的todo -->
<div slot-scope="{ todo }">
<span v-if="todo.isComplete">✓</span>
{{ todo.text }}
</div>
</CompB>
</div>
</template>
<script>
import CompB from './CompB'
export default {
components: {
CompB
},
data() {
return {
todos: [
{
id: 1, text: 'TypeScript', isComplete: true
},
{
id: 2, text: 'JavaScript', isComplete: true
},
{
id: 3, text: 'Node', isComplete: false
}
]
}
}
}
</script>
<!-- CompB.vue -->
<template>
<div>
<h2>This is Comp B</h2>
<ul>
<li
v-for="todo in todos"
v-bind:key="todo.id"
>
<slot v-bind:todo="todo">
</slot>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
todos: {
type: Array,
default: () => []
}
}
}
</script>使用场景:子组件传递数据给父组件时。
祖(父)孙组件通信#
provide 和 inject#
只要在父组件中提供数据,孙组件不管层级多深,都可以注入数据。
// 父组件 提供数据
provide() {
return {
name: 'Hale'
}
}
// 孙组件 注入数据
inject: ['name']使用场景:开发插件或者组件库时使用。
$listeners和$attrs#
现有父组件 A,子组件 B,孙组件 C,通过在组件 B 中绑定$attrs和监听$listeners,使得组件 A 和组件 C 之间可以通信,起到一个桥梁的作用。
<!-- CompA.vue -->
<template>
<div>
<h1>This is Comp A<h1>
<CompB @count="getCount" :message="message"></CompB>
</div>
</template>
<script>
import CompB from './CompB'
export default {
components: {},
data() {
return {
message: 'hello'
}
},
methods: {
getCount(val) {
console.log(val)
}
}
}
</script>
<!-- CompB.vue -->
<template>
<div>
<h2>This is Comp B<h2>
<!-- 绑定`$attrs`和监听`$listeners`-->
<CompC v-on="$listeners" v-bind="$attrs"></CompC>
</div>
</template>
<script>
import CompC from './CompC'
export default {
mounted() {
console.log(this.$listeners) // 可参看$listeners
console.log(this.$attrs) // 可查看$attrs
},
}
</script>
<!-- CompC.vue -->
<template>
<div>
<h3>This is Comp C</h3>
<button @click="emitEvent">Emit Event</button>
</div>
</template>
<script>
export default {
props: ['message'],
methods: {
emitEvent() {
this.$emit('count', 123)
}
}
}
</script>使用场景:创建高级别的组件时。
其他组件通信#
EvenBus#
EventBus 是在非关系型组件之间的通信方式,它主要分为事件的派发和监听。因为 Vue 已经实现了事件派发机制,可以直接使用 Vue 作为中间件。
// 创建并导出Vue的实例 bus.js
import Vue form 'vue'
export default new Vue()
// 事件派发 在组件中引入并派发事件
import bus form '../bus'
bus.$emit('eventName', param)
// 事件监听 在其他组件中引入并监听事件
import bus form '../bus'
bus.$on('eventName', param => {
// ...
})使用场景:非复杂的状态交互但又要用到非关系型组件的通信时。
Vuex#
Vuex 是一个专门为 Vue.js 开发的状态管理库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
使用场景:复杂的状态交互的业务时。