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: computedget 方法本身就会监听依赖的响应式变化(包括异步数据),只要依赖变了,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 暴露方法很麻烦)等。

相关推荐
于慨1 天前
Lambda 表达式、方法引用(Method Reference)语法
java·前端·servlet
石小石Orz1 天前
油猴脚本实现生产环境加载本地qiankun子应用
前端·架构
从前慢丶1 天前
前端交互规范(Web 端)
前端
CHU7290351 天前
便捷约玩,沉浸推理:线上剧本杀APP功能版块设计详解
前端·小程序
GISer_Jing1 天前
Page-agent MCP结构
前端·人工智能
王霸天1 天前
💥别再抄网上的Scale缩放代码了!50行源码教你写一个永不翻车的大屏适配
前端·vue.js·数据可视化
小领航1 天前
用 Three.js + Vue 3 打造炫酷的 3D 行政地图可视化组件
前端·github
@大迁世界1 天前
2026年React大洗牌:React Hooks 将迎来重大升级
前端·javascript·react.js·前端框架·ecmascript
PieroPc1 天前
一个功能强大的 Web 端标签设计和打印工具,支持服务器端直接打印到局域网打印机。Fastapi + html
前端·html·fastapi
悟空瞎说1 天前
深入 Vue3 响应式:为什么有的要加.value,有的不用?从设计到源码彻底讲透
前端·vue.js