Vue JS 将数据从 Parent 传递到 Child 的 Child 的 Child

Vue JS Pass Data From Parent To Child Of Child Of Child

在 Vue.js 中,如何正确地将数据从 parent 组件传递到多级 child 组件链?

您有几个选择:

  1. Props
  2. Event Bus
  3. Vuex
  4. Provide/Inject

在此处了解更多信息:https://www.smashingmagazine.com/2020/01/data-components-vue-js/

@RoboKozo 列出了一些非常适合您的选项,这取决于您对数据的处理方式。

如果它是静态数据(并且你喜欢模块化),你总是可以使用mixins。

来自 Vue.js 文档:

Mixins are a flexible way to distribute reusable functionalities for Vue components. A mixin object can contain any component options. When a component uses a mixin, all options in the mixin will be “mixed” into the component’s own options.

示例 1:混合

File/Folder 设置:

/src/mixins/defaultDataMixin.js // Call this whatever you want

/src/mixins/defaultDataMixin.js:

export const default defaultData {
    data() {
        return {
            property1: "Foo",
            property2: "Bar"
        }
    }
}; 

现在,在您想要访问 property1property2 的任何组件上,您只需导入您的 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 共享数据

道具向下,发射向上

也许您 return 父组件中的某些数据希望传递给子组件使用。道具是做到这一点的好方法!

File/Folder 设置

/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。

虽然它不是直接将数据从父级“传递”到子级,但您可以让父级组件改变子级组件正在监视的状态。

假设您想要 return 用户信息,然后可以跨多个组件访问该数据。

File/Folder 设置:

/.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>