Vue JS + Quasar组件热更新前无法获取props问题求助
问题根源
你的问题出在两个关键环节:
- 页面组件在
onMounted生命周期里才从路由参数赋值给响应式变量,此时SurveyEdit已经完成初始化,拿到的props是初始空值 SurveyEdit内部仅在setup执行时给本地响应式变量赋值一次,没有监听props的后续更新,所以页面组件更新数据后,子组件的表单内容不会同步更新- 热更新时页面组件的响应式变量已经有值,
SurveyEdit初始化就能拿到正确props,因此显示正常
修复方案
1. 页面组件:提前处理路由参数(或监听参数变化)
不需要等到onMounted,直接在setup中处理路由参数,同时可选监听参数变化以支持路由动态更新:
<template> <q-page class="bg-grey-3 column items-center"> <SurveyEdit :question="question" :correctAnswer="correctAnswer" :choices="choices" :imageURL="imageURL" /> </q-page> </template> <script> import SurveyEdit from 'src/components/SurveyEdit.vue' import { ref, watch } from 'vue' import { useRoute } from 'vue-router' export default { name: 'EditPage', components: { SurveyEdit }, setup() { const route = useRoute() const question = ref('') const choices = ref([]) const correctAnswer = ref(null) const imageURL = ref('') // 初始化处理路由参数 const updateFromQuery = (query) => { imageURL.value = query.imageURL || '' question.value = query.question || '' choices.value = query.choices ? query.choices.split('|') : [] correctAnswer.value = query.correctAnswer ? Number(query.correctAnswer) : null } // 首次执行+监听路由参数变化 watch(() => route.query, updateFromQuery, { immediate: true }) return { question, choices, correctAnswer, imageURL, } } } </script>
2. SurveyEdit组件:监听props变化同步本地数据
因为表单需要双向绑定,不能直接修改props,所以要监听props变化,同步到本地响应式变量:
<template> <q-card class="survey-card" flat bordered> <img v-if="imageURL" :src="imageURL"> <q-form @submit="onSubmit" class="q-gutter-md" > <q-file clearable color="orange" standout bottom-slots label="Загрузить изображение для опроса" @update:model-value="onImageLoaded" v-model="loaderModel" class="image-loader" > <template v-slot:prepend> <q-icon name="attach_file" /> </template> <template v-slot:hint> Для удаления изображения нажмите на крестик в правой части загрузчика </template> </q-file> <q-input filled v-model="question" label="Заголовок опроса *" hint="" lazy-rules :rules="[ val => val?.length > 0 || 'Пожалуйста, введите что-нибудь']" /> <ul class="choices-list bg-grey-2"> <q-input v-for="(choice, index) in choices" :key="index" filled label="Ответ *" v-model="choices[index]" hint="" lazy-rules :rules="[ val => val?.length > 0 || 'Пожалуйста, введите что-нибудь']" class="choice" > <template v-slot:append> <q-btn round dense flat icon="close" @click="onDeleteChoice(index)"/> </template> <template v-slot:after> <q-btn :class="{'bg-green-3': index==correctAnswerIndex}" round dense flat icon="check" @click="onSelectCorrectAnswer(index)"/> </template> </q-input> </ul> <div class="column items-center"> <q-btn label="Добавить ответ" color="secondary" class="center" @click="onChoiceAdd"/> </div> <q-separator /> <div> <q-btn label="Сохранить" type="submit" color="primary"/> </div> </q-form> </q-card> </template> <script> import { ref, watch } from 'vue' export default { name: 'SurveyEdit', props: { imageURL: String, question: String, choices: Array, correctAnswer: Number, }, setup(props) { const question = ref(props.question || '') const choices = ref([...(props.choices || [])]) const correctAnswerIndex = ref(props.correctAnswer ?? null) const localImageURL = ref(props.imageURL || '') const loaderModel = ref(null) // 监听props变化,同步本地数据 watch(() => props.question, (newVal) => { question.value = newVal || '' }) watch(() => props.choices, (newVal) => { choices.value = [...(newVal || [])] }, { deep: true }) watch(() => props.correctAnswer, (newVal) => { correctAnswerIndex.value = newVal ?? null }) watch(() => props.imageURL, (newVal) => { localImageURL.value = newVal || '' }) return { loaderModel, imageURL: localImageURL, question, choices, correctAnswerIndex, onImageLoaded(image) { if (image) { localImageURL.value = URL.createObjectURL(image) } }, onSubmit() { console.log(question.value) console.log(choices.value) console.log(correctAnswerIndex.value) }, onChoiceAdd() { choices.value.push('') // 直接push字符串,不要push ref对象 }, onDeleteChoice(index) { choices.value.splice(index, 1) if (index === correctAnswerIndex.value || correctAnswerIndex.value >= choices.value.length ) { correctAnswerIndex.value = null } }, onSelectCorrectAnswer(index) { correctAnswerIndex.value = index } } }, } </script> <style scoped> .image-loader { margin-bottom: 20px; } .choices-list { padding-left: 0px; } .choice { margin-bottom: 20px; } </style>
额外修复点
- 移除了
onChoiceAdd中错误的ref(''),直接push空字符串,避免v-model绑定异常 - 简化了表单校验规则,用可选链
val?.length替代冗余判断 - 给v-for循环添加了
:key属性,符合Vue最佳实践
内容的提问来源于stack exchange,提问作者dudavik
相关产品推荐
相关产品推荐

