Vue 中 v-model 与 props 的"陷阱":为什么加了 props. 就不报错了?

起因

今天使用同事的代码的时候、看见同事代码父组件使用v-model给子组件传递参数呢,但是子组件里面用watch监听了 这个v-model传递的参数、并且赋值给一个ref了 然后又将ref这个使用v-model传递给孙子组件了 我看了半天在想为什么不直接将父组件的这个modelValue直接赋值给孙子组件的v-model呢 于是乎我按照我的想法写了

js 复制代码
const props = defineProps({
  visible: {
    default: false,
  },
});
<template>
  <div>
    <demo v-model:visible="visible" />
  </div>
</template>

当我按下保存键的一瞬间,控制台出现了一段报错

js 复制代码
v-model cannot be used on a prop, because local prop bindings are not writable.
Use a v-bind binding combined with a v-on listener that emits update:x event instead.

我看了一眼、知道这个报错是因为props里面的数据是只读的 不能够更改,违反vue的单项数据流了 于是乎我就改了这样的写法

js 复制代码
<template>
  <div>
    <demo :visible="visible" @update:visible="($event) => (visible = $event)" />
  </div>
</template>

到这里还算正常、但是我又点开了一个组件发现 他竟然这样写不报错、我直接就一个不理解了

js 复制代码
<template>
  <div>
    <demo v-model:visible="props.visible" />
  </div>
</template>

Vue 如何区分模板中的 props 和普通变量

Vue 编译器会建立完整的作用域图谱,包含:

  • 当前组件的 setup() 返回的变量
  • defineProps 定义的 props
  • defineEmits 定义的事件
  • 从父组件继承的 attributes
  • 通过 useAttrs() 访问的属性

当遇到一个变量引用时,Vue 按以下顺序解析:

  1. 本地声明的响应式变量ref/reactive 等)
  2. props 属性 (通过 defineProps 声明)
  3. 上下文属性 (如 slots, attrs
  4. 全局属性 (如 $router, $store

那么我自己的理解的就是你加props和不加props都会找到props里面的数据啊 为什么加了props就报错了 不加就报错了,于是乎我自己就想找到原因

原因

这里其实存在一个显式访问和隐式访问的概念 例如下方代码

js 复制代码
<script setup>
import { ref } from 'vue'
const count = ref(0)
const props = defineProps(['message'])
</script>

<template>
  <div>
    {{ count }} <!-- 本地变量 -->
    {{ message }} <!-- props -->
    {{ props.message }} <!-- 显式props访问 -->
  </div>
</template>

他其实会编译成

js 复制代码
    _ctx.count,        // 直接访问setup变量
    _ctx.message,      // 自动解析为props
    _ctx.props.message // 显式props访问

图例:

所以在这里就出现了一个不同的解析情况 直接使用visible vue是能直接解析出来引用的是props里面的数据 但是 v-model语法糖会编译成 @update:visible=" <math xmlns="http://www.w3.org/1998/Math/MathML"> e v e n t = > ( v i s i b l e = event=>(visible= </math>event=>(visible=event)" 这里直接报错了 props 里是只读的 但是如果你直接写props.visible的话 他是显式访问、vue就知道他是用的props里面的他就会解析成 "@update:visible": ( <math xmlns="http://www.w3.org/1998/Math/MathML"> e v e n t ) = > e m i t ( " u p d a t e : v i s i b l e " , event) => emit("update:visible", </math>event)=>emit("update:visible",event)

这样一个小小的问题困扰到我现在、从摸不着头脑到现在彻底搞明白,几个小时过去了、不容易、总算搞明白了、如果搞不懂今天我绝对睡不好觉,以上写的如有问题请大佬提出、小弟洗耳恭听。

相关推荐
孤水寒月1 小时前
给自己网站增加一个免费的AI助手,纯HTML
前端·人工智能·html
CoderLiu1 小时前
用这个MCP,只给大模型一个figma链接就能直接导出图片,还能自动压缩上传?
前端·llm·mcp
伍哥的传说1 小时前
鸿蒙系统(HarmonyOS)应用开发之实现电子签名效果
开发语言·前端·华为·harmonyos·鸿蒙·鸿蒙系统
海的诗篇_2 小时前
前端开发面试题总结-原生小程序部分
前端·javascript·面试·小程序·vue·html
uncleTom6662 小时前
前端地图可视化的新宠儿:Cesium 地图封装实践
前端
lemonzoey2 小时前
无缝集成 gemini-cli 的 vscode 插件:shenma
前端·人工智能
老家的回忆2 小时前
jsPDF和html2canvas生成pdf,组件用的elementplus,亲测30多页,20s实现
前端·vue.js·pdf·html2canvas·jspdf
半点寒12W2 小时前
uniapp全局状态管理实现方案
前端
Vertira2 小时前
pdf 合并 python实现(已解决)
前端·python·pdf
PeterJXL3 小时前
Chrome 下载文件时总是提示“已阻止不安全的下载”的解决方案
前端·chrome·安全