问题描述
在 Vue.js 中,如何正确地将数据从父组件传递到多级子组件链?
解决方法
您有几个选择:
在此处了解更多信息:https://www.smashingmagazine.com/2020/01/data-components-vue-js/
,@RoboKozo 为您列出了一些非常棒的选择,这取决于您对数据的处理方式。
如果是静态数据(并且你喜欢模块化),你总是可以使用mixin。
来自 Vue.js 文档:
Mixins 是一种为 Vue 组件分发可重用功能的灵活方式。 mixin 对象可以包含任何组件选项。当组件使用 mixin 时,mixin 中的所有选项都会“混合”到组件自己的选项中。
示例 1:混合
文件/文件夹设置:
/src/mixins/defaultDataMixin.js // Call this whatever you want
/src/mixins/defaultDataMixin.js:
export const default defaultData {
data() {
return {
property1: "Foo",property2: "Bar"
}
}
};
现在,在您想要访问 property1
或 property2
的任何组件上,您只需导入您的 mixin。
/src/pages/MyComponent.vue
<template>
<div id="wrapper">
<div class="container">
<div class="row">
<div class="col">
{{ property1 }}
</div>
<div class="col">
{{ property2 }}
</div>
</div>
</div>
</div>
</template>
<script>
import { defaultData } from "@/mixins/defaultDataMixin"
export default {
mixins: [defaultData],}
</script>
示例 2:通过 Props & Emit 共享数据
道具向下,发射向上
也许您在父组件中返回了一些您希望向下传递以在子组件中使用的数据。道具是做到这一点的好方法!
文件/文件夹设置
/src/pages/ParentComponent.vue
/src/components/ChildComponent.vue
ParentComponent.vue
<template>
<div id="wrapper">
<div class="container">
<div class="row">
<div class="col">
<child-component :defaultDataProp="defaultData" @emitted-text="helloWorld">
</child-component>
</div>
</div>
</div>
</div>
</template>
<script>
import { defaultData } from "@/mixins/defaultDataMixin"
import ChildComponent from "@/components/ChildComponent.vue"
export default {
mixins: [defaultData],components: {
ChildComponent
},data() {
return {
emittedText: "",}
},methods: {
helloWorld(e){
this.emittedText = e;
}
}
}
</script>
ChildComponent.vue
<template>
<div id="wrapper">
<div class="container">
<div class="row">
<div class="col">
{{ defaultDataProp }}
</div>
</div>
<div class="row">
<div class="col">
<button @click="emitData">Emit Data</button>
</col>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
defaultDataProp: {
type: String
}
},data() {
return {
text: "Hello,world!"
};
},methods: {
emitData() {
this.$emit('emitted-text',this.text);
}
}
}
</script>
很酷吧?
这些只是您可以在组件之间共享数据和功能的几种方式。
** 编辑 ** 示例 3:Vuex 状态 + mapGetters
我刚刚注意到你也标记了这个 state
,所以我假设你有 already installed 并且正在使用 Vuex。
虽然它不是直接将数据从父组件“传递”到子组件,但您可以让父组件改变子组件正在观看的状态。
假设您想要返回用户信息,然后可以跨多个组件访问该数据。
文件/文件夹设置:
/.env
/src/main.js
/src/store/store.js
/src/store/modules/user.js
/src/mixins/defaultDataMixin.js
/src/util/api.js
/src/pages/ParentComponent.vue
/src/components/ChildComponent.vue
main.js
import Vue from "vue";
import Vuex from "vuex";
import { store } from "@/store/store";
// ... add whatever else you need to import and use
Vue.use(vuex); // but make sure Vue uses vuex
// Vue can "inject" the store into all child components
new Vue({
el: "#app",store: store,// <-- provide the store to the vue instance
// ..
});
.env:
VUE_APP_API_URL='http://127.0.0.1:8000'
api.js:
import axios from "axios";
import { store } from "@/store/store";
const apiClient = axios.create({
baseURL: process.env.VUE_APP_API_URL,});
export default apiClient;
user.js:
import apiClient from "@/util/api"
import { store } from "../store"
const userModule = {
state: () => ({
user: {
firstName: "",}
}),mutations: {
setUser(state,payload) {
state.user = payload;
}
},actions: {
async loadUser({ commit,dispatch }) {
try {
const user = (await apiClient.get("/user")).data;
commit("setUser",user); // mutate the vuex state
} catch (error) {
dispatch("logout");
}
},logout({ commit }) {
commit("setUser",{});
}
},getters: {
firstName: (state) => {
return state.user.firstName;
}
}
};
export default userModule;
store.js
import Vue from "vue";
import Vuex from "vuex";
import userModule from "@/store/modules/user"; // Grab our newly created module
Vue.use(Vuex);
export const store = new Vuex.Store({
modules: {
userState: userModule,},});
defaultDataMixin.js
import { mapGetters } from "vuex";
export const default defaultData {
data() {
return {
property1: "Foo",property2: "Bar"
}
},// We can make use of mapGetters here
computed: {
...mapGetters(["firstName"]),}
};
现在,Parent 和 Child 都可以访问中心化状态并且可以对其进行变异。例如,让我们从父组件调用 loadUser()
并观察子组件的状态变化。
ParentComponent.vue
<template>
<div id="wrapper">
<div class="container">
<div class="row">
<div class="col" v-if="firstName != ''">
{{ firstName }}
</div>
<div class="col">
<button @click="mutateState">Click Me</button>
</div>
</div>
<div class="row">
<div class="col">
<child-component @emitted-text="helloWorld">
</child-component>
</div>
</div>
</div>
</div>
</template>
<script>
import { defaultData } from "@/mixins/defaultDataMixin"
import ChildComponent from "@/components/ChildComponent.vue"
export default {
mixins: [defaultData],methods: {
// our new method to mutate the user state
mutateState() {
this.$store.dispatch("loadUser");
},helloWorld(e){
this.emittedText = e;
}
}
}
</script>
ChildComponent.vue
<template>
<div id="wrapper">
<div class="container">
<div class="row">
<div class="col">
{{ defaultData }}
</div>
</div>
<div class="row">
<div class="col">
<button @click="emitData">Emit Data</button>
</col>
</div>
</div>
</div>
</template>
<script>
import { defaultData } from "@/mixins/defaultDataMixin"
export default {
mixins: [defaultData],this.text);
}
},// Let's watch for the user state to change
watch: {
firstName(newValue,oldValue) {
console.log('[state] firstName state changed to: ',newValue);
// Do whatever you want with that information
}
}
}
</script>