【发布时间】:2021-06-29 15:58:56
【问题描述】:
在 Vue.js 中,如何正确地将数据从父组件传递到多层子组件链?
【问题讨论】:
在 Vue.js 中,如何正确地将数据从父组件传递到多层子组件链?
【问题讨论】:
【讨论】:
@RoboKozo 为您列出了一些非常棒的选择,这取决于您对数据的处理方式。
如果它是静态数据(并且你喜欢模块化),你总是可以使用mixins。
来自 Vue.js 文档:
Mixins 是一种为 Vue 组件分配可重用功能的灵活方式。 mixin 对象可以包含任何组件选项。当组件使用 mixin 时,mixin 中的所有选项都会“混合”到组件自己的选项中。
文件/文件夹设置:
/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>
道具向下,发射向上
也许您在父组件中返回一些您希望传递给子组件的数据。道具是一个很好的方法!
文件/文件夹设置
/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>
很酷,对吧?
这些只是您可以在组件之间共享数据和函数的几种方式。
我刚刚注意到你也标记了这个 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],
components: {
ChildComponent
},
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],
data() {
return {
text: "Hello, world!"
};
},
methods: {
emitData() {
this.$emit('emitted-text', 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>
【讨论】: