Vue组合式API中props目标只读报错:组件传值异常
解决Vue组件props修改报错:Set operation on key "texteEnvoye" failed: target is readonly
这个错误的根源很明确:你在Visionneuse组件的watch回调里尝试直接修改从props转换来的ref,而props在Vue里是只读的,子组件不能直接修改父组件传递过来的props值。
看你代码里的这段watch逻辑:
watch(texte, (_, y) => { texte.value = y; initCarte(); });
这里的texte是通过toRefs(props)生成的,本质上还是绑定了父组件的texteEnvoye props。你在这里执行texte.value = y(把旧值重新赋值回去)完全没有必要,还触发了Vue的只读限制报错。
修复方案
只需要移除这个多余的赋值操作即可解决报错。如果后续子组件需要维护文本的本地修改状态,可以额外创建本地ref来承接props值,具体调整如下:
调整后的Visionneuse组件setup部分:
setup(props) { const myCanvas = ref(null); const carte = ref(null); // 创建本地ref承接props值,后续如需修改文本操作这个本地变量 const localTexte = ref(props.texteEnvoye); const { taille } = toRefs(props); const rapport = ref(0); const petit = ref((window.innerWidth < 750)); const size = reactive({ w: window.innerWidth * taille.value, h: window.innerWidth * taille.value, }); // 保留你的drawText、initCarte、handleResize函数,注意drawText里的文本要改用localTexte function drawText() { const fontSize = 0.05 * size.w - 10; myCanvas.value.font = `${fontSize}px Adrip`; myCanvas.value.textAlign = 'center'; myCanvas.value.fillStyle = 'lightgrey'; myCanvas.value.strokeStyle = 'black'; myCanvas.value.lineWidth = 0.006 * size.w - 10; const x = size.w / 2; const lineHeight = fontSize; // 改用本地的localTexte const lines = localTexte.value.split('\n'); for (let i = 0; i < lines.length; i += 1) { myCanvas.value.fillText( lines[lines.length - i - 1], x, (size.h * 0.98) - (i * lineHeight), ); myCanvas.value.strokeText( lines[lines.length - i - 1], x, (size.h * 0.98) - (i * lineHeight), ); } } function initCarte() { const background = new Image(); background.src = '/img/fond.jpeg'; background.onload = function () { rapport.value = background.naturalWidth / background.naturalHeight; size.h = size.w / rapport.value; nextTick(() => { try { myCanvas.value.drawImage(background, 0, 0, size.w, size.h); } catch (e) { console.log(`ERREUR DE CHARGEMENT D'IMAGE: ${e}`); } if (!petit.value) { drawText(); } }); }; } function handleResize() { size.w = window.innerWidth * taille.value; size.h = size.w / rapport.value; petit.value = window.innerWidth < 750; initCarte(); } window.addEventListener('resize', handleResize); // 监听props的文本变化,同步更新本地ref并重新绘制 watch(() => props.texteEnvoye, (newVal) => { localTexte.value = newVal; initCarte(); }); onMounted(() => { const c = carte.value; const ctx = c.getContext('2d'); myCanvas.value = ctx; initCarte(); }); return { myCanvas, size, localTexte, petit, carte, }; }
关键注意点:
- 永远不要直接修改props:props是单向数据流,父组件传递给子组件,子组件只能读取,不能修改。如果需要修改,要么通过
emit通知父组件更新,要么在子组件内创建本地副本维护状态。 - 原watch里的赋值操作完全冗余,移除后即可解决当前报错;如果后续有子组件修改文本的需求,一定要用本地ref来承接props值。
内容的提问来源于stack exchange,提问作者djcaesar9114
相关产品推荐
相关产品推荐

