一、Vue 2

父组件中的代码:

<template>
<div>
<child-component :prop-a="dataA"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent,
},
data() {
return {
dataA: 'data from parent',
};
},
};
</script>

子组件中的代码:

<template>
<div>
{{ propA }}
</div>
</template>
<script>
export default {
props: {
propA: String,
},
};
</script>

子组件中的代码:

<template>
<div>
<button @click="sendDataToParent">Send Data To Parent</button>
</div>
</template>
<script>
export default {
data() {
return {
dataB: 'data from child',
};
},
methods: {
sendDataToParent() {
this.$emit('send-data', this.dataB);
},
},
};
</script>

父组件中的代码:

<template>
<div>
<child-component @send-data="receiveDataFromChild"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent,
},
methods: {
receiveDataFromChild(dataB) {
console.log(dataB);
},
},
};
</script>

父组件中的代码:

<template>
<div>
<child-a :prop-a="dataA"></child-a>
<child-b :prop-b="dataB"></child-b>
</div>
</template>
<script>
import ChildA from './ChildA.vue';
import ChildB from './ChildB.vue';
export default {
components: {
ChildA,
ChildB,
},
data() {
return {
dataA: 'data from parent to child a',
dataB: 'data from parent to child b',
};
},
};
</script>

子组件 A 中的代码:

<template>
<div>
{{ propA }}
</div>
</template>
<script>
export default {
props: {
propA: String,
},
};
</script>

子组件 B 中的代码:

<template>
<div>
{{ propB }}
</div>
</template>
<script>
export default {
props: {
propB: String,
},
};
</script>

祖先组件中的代码:

<template>
<div>
<child-a></child-a>
</div>
</template>
<script>
import ChildA from './ChildA.vue';
export default {
components: {
ChildA,
},
provide() {
return {
sharedData: 'data from ancestor',
};
},
};
</script>

子孙组件 A 中的代码:

<template>
<div>
{{ sharedData }}
</div>
</template>
<script>
export default {
inject: ['sharedData'],
};
</script>

在 main.js 中定义一个空的 Vue 实例作为事件总线:

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

子组件 A 中的代码:

<template>
<div>
<button @click="sendDataToSibling">Send Data To Sibling</button>
</div>
</template>
<script>
import { bus } from './main';
export default {
methods: {
sendDataToSibling() {
bus.$emit('send-data', 'data from child a');
},
},
};
</script>

子组件 B 中的代码:

<template>
<div>
{{ dataFromSibling }}
</div>
</template>
<script>
import { bus } from './main';
export default {
data() {
return {
dataFromSibling: '',
};
},
mounted() {
bus.$on('send-data', (data) => {
this.dataFromSibling = data;
});
},
};
</script>

在 store.js 中定义一个 Vuex store:

import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
dataC: 'data from Vuex',
},
mutations: {
updateDataC(state, payload) {
state.dataC = payload;
},
},
});
export default store;

子组件 A 中的代码:

<template>
<div>
<button @click="sendDataToSibling">Send Data To Sibling</button>
</div>
</template>
<script>
import { mapMutations } from 'vuex';
export default {
methods: {
...mapMutations(['updateDataC']),
sendDataToSibling() {
this.updateDataC('data from child a');
},
},
};
</script>

子组件 B 中的代码:

<template>
<div>
{{ dataC }}
</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
computed: {
...mapState(['dataC']),
},
};
</script>

$attrs 是一个包含了父组件传递给子组件的所有属性的对象,可以在子组件中通过访问 $attrs 来获取这些属性。如果不希望某些属性传递到子组件中,可以在子组件中使用 v-bind="$attrs" 并指定排除的属性名称,或者在父组件中使用 .sync 修饰符,将属性绑定到子组件的一个名为 $attrs 的属性上。

$listeners 是一个包含了父组件传递给子组件的所有事件监听器的对象,可以在子组件中通过访问 $listeners 来获取这些事件监听器。如果需要在子组件中监听某个事件,可以使用 v-on="$listeners" 将所有的事件监听器绑定到子组件上。

$attrs 和 $listeners 是常用的两个特殊属性,它们可以用来向组件传递属性和事件监听器。假设我们有一个父组件和一个子组件,子组件需要接收父组件的一些属性和事件监听器,同时还需要把这些属性和事件传递给子组件的某个子元素。

父组件中的代码:

<template>
<div>
<child-component :title="title" v-on:click="handleClick" />
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: { ChildComponent },
data() {
return {
title: 'Hello World',
};
},
methods: {
handleClick() {
console.log('Button Clicked');
},
},
};
</script>

子组件中的代码:

<template>
<div>
<button @click="$emit('click')">Click me</button>
<div v-bind="$attrs">
<slot />
</div>
</div>
</template>
<script>
export default {
inheritAttrs: false,
props: {
title: {
type: String,
default: '',
},
},
mounted() {
console.log(this.$attrs);
console.log(this.$listeners);
},
};
</script>

在子组件中,我们使用 v-bind="$attrs" 把所有父组件传递过来的属性绑定到子元素上。同时,我们使用 $emit('click') 来触发父组件传递过来的点击事件。

在子组件 中,需要设置 inheritAttrs: false,来禁止自动将父组件传递的属性绑定到子组件的根元素上。这样,我们就可以使用 v-bind="$attrs" 把所有属性绑定到子元素上。

在 mounted 钩子中,我们可以通过 this.$attrsthis.$listeners 来分别访问所有属性和事件监听器。这样,我们就可以在子组件中使用这些属性和事件了。

二、Vue 3

Props 是一种在组件之间传递数据的方式,通过在组件标签上使用属性绑定,父组件可以向子组件传递数据。在子组件中,通过在 props 中定义对应的属性名,可以获取到父组件传递过来的数据。

例如,父组件中的模板:

<template>
<child-component :message="hello"></child-component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
hello: 'Hello from parent!'
};
}
};
</script>

子组件中的模板:

<template>
<div>{{ message }}</div>
</template>
<script>
export default {
props: {
message: String
}
};
</script>

$emit 是一种在子组件中触发事件的方式,通过在子组件中使用 $emit 方法,可以向父组件发送数据。在父组件中,通过在子组件标签上使用 v-on@ 语法,可以监听子组件触发的事件,并获取子组件发送的数据。

例如,子组件中的模板:

<template>
<button @click="sendMessage">Send Message</button>
</template>
<script>
export default {
methods: {
sendMessage() {
this.$emit('message-sent', 'Hello from child!');
}
}
};
</script>

父组件中的模板:

<template>
<child-component @message-sent="receiveMessage"></child-component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
receiveMessage(message) {
console.log(message);
}
}
};
</script>

Provide/Inject 是一种在祖先组件和后代组件之间共享数据的方式。通过在祖先组件中使用 provide 方法提供数据,在后代组件中使用 inject 方法获取数据。

例如,祖先组件中的模板:

<template>
<child-component></child-component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
provide() {
return {
message: 'Hello from ancestor!'
};
}
};
</script>

后代组件中的模板:

<template>
<div>{{ message }}</div>
</template>
<script>
export default {
inject: ['message']
};
</script>

$attrs 和 $listeners 是在 Vue 2 中引入的特性,但在 Vue 3 中也得到了支持。

例如,父组件中的模板:

<template>
<child-component message="Hello from parent!" @click="handleClick"></child-component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
handleClick() {
console.log('Clicked!');
}
}
};
</script>

子组件中的模板:

<template>
<div v-bind="$attrs" v-on="$listeners">{{ message }}</div>
</template>
<script>
export default {
props: {
message: String
}
};
</script>

在 Vue 3 中,provide 和 inject 可以与 props 结合使用,从而实现一种高级的数据传递方式。具体做法是,在祖先组件中使用 provide 方法提供数据,并在后代组件中使用 inject 方法获取数据;同时,在后代组件中,可以在 props 中声明和接收数据,从而实现数据的类型检查和默认值设定。

例如,祖先组件中的模板:

<template>
<child-component></child-component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
provide() {
return {
message: 'Hello from ancestor!'
};
}
};
</script>

后代组件中的模板:

<template>
<div>{{ message }}</div>
</template>
<script>
export default {
inject: ['message'],
props: {
message: {
type: String,
default: 'Hello from default!'
}
}
};
</script>

在上面的例子中,子组件会首先从祖先组件中获取名为 message 的数据,如果没有提供,则使用默认值 Hello from default!。在子组件中,props 会覆盖 provide/inject,因此如果父组件和子组件都提供了同一个属性,子组件中的 props 值会覆盖 provide/inject 中的值。

Vuex 是一种专门用于管理应用程序状态的库,可以用于跨组件传递数据。在 Vuex 中,可以定义一个全局的状态管理器,所有的组件都可以通过 getter 和 setter 方法访问和修改这个状态管理器中的数据。

例如,定义一个 Vuex store:

import { createStore } from 'vuex';
const store = createStore({
state: {
message: 'Hello from store!'
},
mutations: {
updateMessage(state, message) {
state.message = message;
}
},
getters: {
getMessage(state) {
return state.message;
}
}
});
export default store;

在组件中使用 Vuex:

<template>
<div>{{ message }}</div>
<button @click="updateMessage">Update message</button>
</template>
<script>
import { mapGetters, mapMutations } from 'vuex';
export default {
computed: {
...mapGetters(['getMessage'])
},
methods: {
...mapMutations(['updateMessage'])
}
};
</script>

在这个例子中,组件通过 mapGetters 方法将 Vuex store 中的 getMessage 方法映射为组件中的计算属性,从而获取 Vuex store 中的数据;同时,通过 mapMutations 方法将 Vuex store 中的 updateMessage 方法映射为组件中的方法,从而修改 Vuex store 中的数据。

EventBus 是一种自定义事件总线,可以用于在任意组件之间传递数据。在 EventBus 中,可以定义一个全局的事件中心,所有的组件都可以通过 $on 和 $emit 方法监听和触发自定义事件。

例如,定义一个 EventBus:

import mitt from 'mitt';
const bus = mitt();
export default bus;

在组件中使用 EventBus:

<template>
<div>{{ message }}</div>
<button @click="updateMessage">Update message</button>
</template>
<script>
import bus from './event-bus';
export default {
data() {
return {
message: 'Hello from component!'
};
},
methods: {
updateMessage() {
this.message = 'New message!';
bus.emit('message-updated', this.message);
}
},
created() {
bus.on('message-updated', message => {
console.log(message);
});
}
};
</script>

在这个例子中,组件中的 updateMessage 方法通过 EventBus 的 emit 方法触发了一个名为 message-updated 的自定义事件,并将修改后的消息作为参数传递给事件处理函数;同时,在组件的 created 生命周期钩子中,通过 EventBus 的 on 方法监听名为 message-updated 的自定义事件,并在事件处理函数中打印接收到的消息。

总之,Vue 3 中组件之间传值的方式很多,可以根据具体的场景选择最适合的方式。使用 props 和 $emit 可以实现父子组件之间的传值,使用 provide 和 inject 可以实现祖先组件向后代组件的传值,使用 Vuex 和 EventBus 可以实现任意组件之间的传值。在选择组件传值方式时,还应该考虑数据的安全性、可维护性和性能等因素。