为什么在尝试将异步 API 数据作为 prop 从父组件传递到子组件时出现未定义错误?

Why do I get Undefined error when trying to pass async API data as prop from parent to child component?

有人能告诉我为什么在尝试将 prop 向下传递给子组件时得到 undefined 吗?

错误:Cannot read properties of undefined (reading 'title')

沙盒再现:https://codesandbox.io/s/little-silence-3lgd9?file=/components/PostEditor.vue

父组件:

<template>
  <div>
    <PostEditor v-bind="post" />
  </div>
</template>

<script>
export default {
  name: "PostsEdit",
  data() {
    return {
      post: {},
    };
  },
  async created() {
    const response = await fetch(
      "https://jsonplaceholder.typicode.com/posts/1"
    );
    this.post = await response.json();
  },
};
</script>

子组件:

<template>
  <section>
    {{ title }}
  </section>
</template>

<script>
export default {
  post: {
    type: Object,
    default: () => {},
    required: false,
  },
  data() {
    return {
      title: this.post.title,
    };
  },
  methods: {
    save() {
      this.$emit("save", {
        ...this.formValues,
      });
    },
  },
};
</script>

子组件中需要先定义props段,然后通过computed

访问
export default {
 props: { // change added
   post: {
    type: Object,
    default: () => {},
    required: false,
   }
  }, // change added
  computed: {
    title() {
      return this.post ? this.post.title : '',
    }
  },

v-bind="post"

child 属性名为“post”,但 parent 正试图将多个属性与 v-bind="post" 绑定。

绑定名称应与 child 中的目标道具相匹配:

<!-- <PostEditor v-bind="post" /> --> ❌ binds subproperties of post

<PostEditor v-bind:post="post" />     ✅ binds post
<PostEditor :post="post" />           ✅ binds post (shorthand)

post 默认道具值

child 的 post 属性有一个 default 选项 () => {},但是箭头函数 return 什么都没有(undefined), 因为大括号开始一个 块作用域 。这实际上与没有 default 选项相同。

您的意思可能是 default 到 return 一个 空 object,这需要将大括号括在 parent 中他:

// default: () => {}   // ❌ returns undefined
default: () => ({})    // ✅ returns {}

data() 无反应

即使上面固定了 prop default,data() 中的 formValues.title 属性 也被初始化为 this.post.title,这将是 undefined 因为 post 最初是一个空 object。从parent传进来的post值是异步更新的,所以在[=22=中还是parent的初始值(也是一个空的object) ].

请注意 data() 不是反应式的,因此它只在初始化时调用一次。对 this.post 的更改将 不会 自动更新数据 属性。

解决方案:在 post 填充后渲染 child

一个解决方案是推迟渲染 child 组件,直到 post 道具填充到 parent 中,这样 post 道具将具有预期值child 的 data() 初始化。

在parent中,初始化postnull,并使用v-if="post"条件渲染child:

<template>
  <div>                             
    <PostEditor :post="post" v-if="post" />
  </div>
</template>

<script>
export default {
  data() {
    return { 
      post: null,
    }
  },
}
</script>

demo