首页 > 解决方案 > Vue3 - 在组件内提供/注入对象后丢失数据

问题描述

我在我的项目中提供/注入有小问题。

在 App.vue 中,我从数据库中提取数据并将其推送到对象中。使用控制台日志,我检查了所有数据,所有数据都在那里。

<template>
  <router-view />
</template>

<script>
export default {
  provide() {
    return {
      user: this.user,
    };
  },

  data() {
    return {
      user: '',
    };
  },
  methods: {
      ///pulling data from DB
    func() {
      fetch("url")
        .then((response) => {
          if (response.ok) {
            return response.json();
          }
        })
        .then((data) => {
          const user = [];
          for (const id in data) {
            user.push({
              id: data[id].user_id,
              firstName: data[id].user_firstname,
              lastName: data[id].user_lastname,
              email: data[id].user_email,
              phone: data[id].user_phone,
              address1: data[id].user_address_1,
              address2: data[id].user_address_2,
              address3: data[id].user_address_3,
              address4: data[id].user_address_4,
              group: data[id].user_group,
            });
          }
          this.user = user;
        })
        .catch((error) => {
          console.log(error);
        });
    },
  },
  created() {
    this.func();
  },
};
</script>

对象用户 App.vue 的控制台日志

Object { id: "3", firstName: "test", lastName: "test", … }


接下来我将它注入到组件中。组件内的对象存在,但为空 - 所有数据不复存在。


<script>
export default {
  inject: ["user"],
};
</script>

组件中对象用户的控制台日志

<empty string>

虽然在 App.vue 中数据仍然存在,但在任何组件对象中似乎都是空的,但它确实存在。知道为什么吗?感谢帮助。

标签: vue-routervuejs3

解决方案


简而言之,这是因为您正在重新分配用户而不是更改用户。

假设您有一个 Child 组件,它使用您的inject数据并将用户呈现在列表中:

<template>
  <div> Child </div>
  <ul>
    <li v-for="user in users" :key="user.id"> {{user.name}} </li>
  </ul>
</template>

<script>
  import {inject} from "vue";
  
  export default {
    name: "Child",
    
    setup() {
      const users = inject("users");
      return {users};
    }
}
</script>

要提供usersfrom parent 组件,您需要确保它users本身是一个响应式对象,并且您不断地从父级更改它而不是重新分配它。

我将使用组合 API 来说明我的意思。与 options api 相比,composition api 中的所有内容都只是普通的 javascript,因此幕后的魔力要少得多。最后我会告诉你options api是如何与composition api相关的。

<template>
  <button @click=generateUsers>
      Generate Users
  </button>
  <Child/>
</template>

<script>
  import {reactive, provide, toRefs} from "vue";
  import Child from "./Child.vue";
  
  export default {
    name: "App",
    components: {
      Child
    },
    
    setup() {
      const data = reactive({users: ""});
      
      const generateUsers = () => {
        // notice here you are REASSIGNING the users
        data.users = [
          {id: 1, name: "Alice"}, {id: 2, name: "Bob"}
        ];
        
        console.log(data.users);
      }
      
      // this way of provide will NOT work 
      provide("users", data.users);
      
      // this way works because of toRefs
      const {users} = toRefs(data);
      provide("users", users);
      
      return {generateUsers};
    }
}
</script>

需要注意的几点:

  1. dataoptions api 中的选项与const data = reactive({users: ""}). Vue 将运行你的 data() 方法,你必须从那里返回一个普通的 object。然后 Vue 会自动调用reactive来为其添加响应性。
  2. provide另一方面,它并没有做任何魔术 - 无论是在选项 api 中,还是在组合 api 中。它只是将提供给消耗组件的任何内容传递给消耗组件,而无需任何按摩。
  3. 原因provide("users", data.users)不像您预期​​的那样起作用,因为您填充用户的方式不是对同一data.users对象的更改(实际上是响应式的),而是一起重新分配。
  4. 之所以toRefs有效是因为 toRefs链接到原始父级。

考虑到这种理解,要修复您的原始代码,您只需要确保您更改而不是重新分配用户。最简单的方法是将 user 定义为一个数组,并在加载数据时将其推入其中。(与最初将其定义为字符串并稍后重新分配它相反)


PS 也适用于组合 api,并且更简单的是:

<template>
    <button @click=generateUsers>
      Generate Users
  </button>
  <Child/>
</template>

<script>
  import {ref, provide} from "vue";
  import Child from "./Child.vue";
  
  export default {
    name: "App",
    components: {
      Child
    },
    
    setup() {
            
      const users = ref();
      
      const generateUsers = () => {
        // notice here you are not reassigning the users
        // but CHANGING its value
        users.value = [
          {id: 1, name: "Alice"}, {id: 2, name: "Bob"}
        ];

        console.log(users.value);
      }

      provide("users", users);

      
      return {generateUsers};
    }
}
</script>

推荐阅读