Vue.js 3:发出事件导致错误
我尝试从子组件向其父组件发出事件。我已阅读 vuejs.org 指南 并认为我的语法正确。我的子组件 (AxisLabel.vue) 有
<script setup>
....
const emit = defineEmits(['removeLetter', 'submitLetter'])
const props = defineProps({
stat: Object,
index: Number,
segments: Number
})
const point = computed(() =>
{
try {
return valueToPoint(props.stat.value - 10, props.index, props.segments, true)
}
catch {
return valueToPoint(0, 0, 9, true)
}
})
function click() {
if (props.stat.chosen) {
props.stat.chosen = false;
emit('removeLetter',props.stat.label)
}
else {
props.stat.chosen = true;
emit('submitLetter',props.stat.label)
}
}
....
</script>
<template>
<text @click="click" :class=changeColour() class="prevent-select"
:x="point.x" :y="point.y">{{stat.label}}
</text>
</template>
并且在父组件 (app.vue) 中我有:
<script setup>
import AxisLabel from './components/AxisLabel.vue'
const tryingWord = ref('word')
function addToWord(letter) {
tryingWord.value = tryingWord.value + letter
}
function removeFromWord(letter) {
// ....todo....
}
</script>
<template>
<svg width="300" height="300">
<PolyGraph :stats="stats"></PolyGraph>
</svg>
<text class="prevent-select" rows="1" cols="9">
{{tryingWord}}
</text>
<AxisLabel @submit-letter = "addToWord"/>
<AxisLabel @remove-letter = "removeFromWord"/>
</template>
当我运行此代码时,我得到
[Vue warn]: Unhandled error during execution of render function at <AxisLabel onSubmitLetter=fn > at
随后出现“未定义”错误(见下文)。应用程序启动后就会发生这种情况,即没有触发点击事件。
如果我从 app.vue 中删除事件侦听器定义,则不会发生错误。
[稍后]
我现在看到,
没有
定义事件侦听器,AxisLabel 的
<text>
元素被调用了 9 次(应该是这样的) - 它是从另一个组件调用的:
<axis-label
v-for="(stat, index) in stats"
:stat="stat"
:index="index"
:segments="stats.length-1">
并且“stats”有 9 个成员。但是当我重新添加事件监听器时,该元素会尝试创建 第十次 ,并使用无效参数,从而导致崩溃 - Chrome 调试器指示此处的“props”未定义:
<text @click="click" :class=changeColour() class="prevent-select"
:x="point.x" :y="point.y">{{stat.label}}
</text>
“props”用于计算文本的位置,但在使用它的地方添加 try/catch 并没有帮助。
我需要查看在 vue 中发出事件的 有效 示例 - vuejs.org 似乎没有提供一个。
您的事件处理程序函数内部有错误。我不确定这是否是问题所在,但可能是。
const tryingWord = ref('word')
function addToWord(letter) {
// when working with refs, you need to use the .value property
tryingWord.value = tryingWord.value + letter
}
此外,您永远不应像这里一样改变从 props 传递的数据:
function click() {
if (props.stat.chosen) {
// this mutates the props
props.stat.chosen = false;
emit('removeLetter',props.stat.label)
}
else {
// this mutates the props
props.stat.chosen = true;
emit('submitLetter',props.stat.label)
}
}
您将在 vue 文档 中找到有关单向数据流的解释。
更新
正如评论中提到的,
AxisLabel
组件没有收到任何 props,这将导致在访问
<script setup>
内的 props 时出现错误。
// the following line will throw an error
// because props.stat === undefined
if (props.stat.chosen) {
// ...
}
您可以定义一个在未传递任何属性时使用的默认属性,也可以检查 prop 是否在使用之前未定义。
问题出在这里:
<template>
<svg width="300" height="300">
<PolyGraph :stats="stats"></PolyGraph>
</svg>
<text class="prevent-select" rows="1" cols="9">
{{tryingWord}}
</text>
<AxisLabel @submit-letter = "addToWord"/>
<AxisLabel @remove-letter = "removeFromWord"/>
</template>
实际上有 2 个错误:首先,对 Axislabel 的调用有 2 次,但应该只有一次:
<AxisLabel @submit-letter = "addToWord" @remove-letter = "removeFromWord"/>
其次,由于 Axislabel 是 PolyGraph 的子级,我需要将事件通过那里向上传播以到达
App.vue
,即这些事件处理程序应该在
PolyGraph.vue
中定义,而不是在
App.vue