vue3:组件中,v-model的区别(新版)

一、v-model 传值的核心本质

v-model 是 语法糖,本质是「父组件传值 + 子组件触发更新事件」的组合:

  • 对原生表单元素:v-model="xxx" ≈ :value="xxx" @input="xxx = $event.target.value"
  • 对自定义组件:v-model="xxx" ≈ :modelValue="xxx" @update:modelValue="xxx = $event" ("update:modelValue"仅仅是个事件名而已)

这个规则是所有用法的基础,记住它就能灵活应对各种场景。

二、场景:父子组件的 v-model 传值(核心重点)

1、Vue的单向数据流,禁止子组件直接修改 props(包括用 v-model 绑定)因为props是只读的;

2、子组件不能直接给 props.modelValue 绑 v-model,必须通过「内部变量(ref) / 计算属性(computed)」中转,因为v-model 是 语法糖;

默认传值(modelValue)

js 复制代码
<!-- 父组件 Father.vue -->
<template>
  <input type="number" v-model="msg">
  <Child v-model="msg"></Child>
</template>
<script setup>
import { ref } from 'vue'
import Child from './components/test.vue'
const msg = ref(100)
// 仿照接口的异步数据
setTimeout(() => {
  msg.value = 2000
}, 1000)
</script>
js 复制代码
<!-- 子组件 Child.vue -->
<template>
    <input 
    v-model="innerValue"
  />
</template>
<script setup>
import { ref, watch } from 'vue';

const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const innerValue = ref(props.modelValue)

// 监听外部变量父组件props变化 → 同步到本地变量(父 → 子)
// immediate: true 确保初始化时也能触发(如果是异步数据必须加,不然初始化数据一直是100,同步数据可不加)
watch(() => props.modelValue, (newVal) => {
  innerValue.value = newVal
}, { immediate: true })

//  监听本地变量子组件的数据变化 → 触发emit通知父组件(子 → 父)
watch(innerValue, (newVal) => {
  emit('update:modelValue', newVal)
})
</script>

Tips: 因为仿照接口的异步数据,所以16-18行代码必须加),通过v-model和watch的结合,父子组件的值,双向绑定效果就出来了。

js 复制代码
<!-- 父组件 Father.vue -->
<template>
  <div>
      <Test v-model="message"></Test>
      <span>父传子:<input type="number" v-model="message"></span>
  </div>
</template>

<script setup >
import { ref } from 'vue';
import Test from './components/test.vue'
const message = ref(100);
setTimeout(() => {
  message.value = 20000
},1000);
</script>
js 复制代码
<!-- 子组件 Child.vue -->
<template>
  <div>
    子传父:<input type="number" v-model="message">
  </div>
</template>

<script setup>
import { computed, ref, watch } from 'vue';
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
  modelValue: Number
})
const emits = defineEmits(['update:modelValue']) 
// 不能直接修改computed的值,会报错:[Vue warn] Write operation failed: computed value is readonly
// const message = computed(() => props.modelValue) // 报错
const message = computed({
  get(){
    return props.modelValue
  },
  set(val){
    emits('update:modelValue', val)
  }
})
</script>

Tips: computed 的 get 方法本身就会监听依赖的响应式变化(包括异步数据),只要依赖变了,computed 的值就会自动更新,所以不用像watch那么复杂监听2次。

js 复制代码
<!-- 父组件 -->
<template>
  <el-button type="primary" @click="openInnerDialog">
    Open the inner Dialog
  </el-button>
  <InnerDialog v-model="innerVisible" />
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import InnerDialog from './components/test.vue'

const innerVisible = ref(false)
const openInnerDialog = () => {
  innerVisible.value = true
}

</script>
js 复制代码
<!-- 子组件 -->
<template>
  <el-dialog
    v-model="visible"
    width="500"
    title="Inner Dialog"
    append-to-body
  >
    <span>This is the inner Dialog</span>
  </el-dialog>
</template>

<script lang="ts" setup>
import { computed } from 'vue'
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const visible = computed({
  get: () => props.modelValue, 
  set: (val) => emit('update:modelValue', val)
})

</script>

Tips:弹框组件的常见用法:props和emit(缺点:父组件,每个入口触发 emit)、ref(缺点:父组件直接操作子组件实例,耦合性高,不符合 "单向数据流"、Vue3 中需手动 defineExpose 暴露方法很麻烦)等。

相关推荐
羲云5 分钟前
Claude 越界事件之后:Agent 权限设计为什么不能只靠提示词
前端
行者全栈架构师19 分钟前
【鸿蒙心迹】鸿蒙网络请求架构实战——@ohos.net.http 到 Axios 封装、拦截器与统一错误处理(HarmonyOS 7.x)
前端·算法·架构
全栈Agent 小李26 分钟前
【无标题】
前端·后端·agent·ai编程·全栈·cursor·mcp
羲云26 分钟前
别让 AI 凭记忆做调研:给 WorkBuddy 接上可核验的实时搜索
前端
deli0071 小时前
连接池到底配多大?QPS 和 RT 算一遍,再跑 1 万次请求实测超时率
前端
风花一世月1 小时前
🏮 我把三坊七巷做成了能飞的 3D 地图(901 个真实建筑轮廓、20 处景点,在线直接玩)
前端
特立独行的猫A1 小时前
用仓颉语言写一个串口调试助手:cj-tauri 实战
前端
小凯在掘金1 小时前
Shadow DOM 和 Virtual DOM ?别再傻傻分不清
前端·html
郑州光合科技余经理1 小时前
本地生活平台搭建:跨业态用户标识怎么贯通
java·开发语言·前端·后端·uni-app·php·ai编程
数据掘金1 小时前
埋点事件命名升级时老数据怎么办
前端