Appearance
1. 核心原理:v-model 的语法糖本质
在 Vue 3 中,v-model 不再是黑盒,它等同于 属性绑定 (Props) + 事件监听 (Emits)。
- 父组件写法:
<Child v-model:initialCounter="total" />
//等同于
<Child
:initialCounter="total"
@update:initialCounter="val => total = val"
/>- 知识点:
v-model后面可以接冒号传参数(如:initialCounter),这意味着一个组件可以同时绑定多个v-model。
2. 现代写法:defineModel (Vue 3.4+)
这是目前最推荐的方式,它极大地简化了子组件的代码,自动处理了 props 声明和 emit 触发。
子组件实现:
JavaScript
// 声明一个双向绑定的 ref const counter = defineModel('initialCounter'); // 修改时,父组件同步更新,无需手动 emit const add = () => counter.value++;
3. 传统写法:Vue 3.4 以前的方案
在没有 defineModel 时,需要手动建立子组件内部状态与父组件 props 的联系。
⚠️ 错误做法: const c = ref(props.prop) (会导致数据流断裂,仅初始化一次)。
| 方案 | 代码片段 (子组件) | 适用场景 |
|---|---|---|
computed | const c = computed({ get:()=>props.p, set:(v)=>emit('update:p', v) }) | 最推荐。逻辑清晰,支持在 set 中做数据校验。 |
watch | watch(()=>props.p, (v)=>local.value=v) | 适合在数据变化时需要执行异步操作或复杂逻辑。 |
toRef | const c = toRef(props, 'p') | 保持引用。但注意:由于单向数据流,子组件不能直接修改 c.value。 |
4. 经验总结
单向数据流:无论哪种方案,最终目的都是为了遵循“父变子随,子改父定”的原则。
数据校验:如果需要对子组件输入的值做限制(如最大最小值),建议使用
computed的set或watch模式进行拦截,校验通过后再发送emit。