为什么在尝试将异步 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中,初始化post
为null
,并使用v-if="post"
条件渲染child:
<template>
<div>
<PostEditor :post="post" v-if="post" />
</div>
</template>
<script>
export default {
data() {
return {
post: null,
}
},
}
</script>
有人能告诉我为什么在尝试将 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中,初始化post
为null
,并使用v-if="post"
条件渲染child:
<template>
<div>
<PostEditor :post="post" v-if="post" />
</div>
</template>
<script>
export default {
data() {
return {
post: null,
}
},
}
</script>