You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue2中v-model做了哪些额外处理?两种绑定写法为何不等价

问题对应示例代码

<template>
  <div>
    value: {{ obj.c }}
    <my-input v-model="obj.c"/>
    <my-input :value="obj.c" @change="(val) => (obj.c = val)"/>
  </div>
</template>

<script>
export default {
  data(){
    return {
      obj: {}
    }
  }
}
</script>

其中my-input为自定义组件,实际运行时v-model绑定写法可以正常更新视图,但手动通过:value传值+@change事件赋值的写法无法触发视图更新。

结论

这两种写法并不完全等价,表现差异来自Vue2响应式系统的固有特性,以及v-model在编译阶段做的额外封装处理。

手动写法失效的根本原因

Vue2的响应式实现基于Object.defineProperty,只有初始化阶段就已经在data中声明的属性,才会被getter/setter劫持,成功收集视图依赖、触发更新。
示例代码中,data里返回的obj初始是空对象,没有提前声明c属性,手动实现的@change="(val) => (obj.c = val)"是裸赋值操作,本质是给响应式对象obj新增了一个从来没被劫持过的属性c,这个操作在Vue2中默认无法触发视图更新——新增的属性没有对应的setter通知watcher更新,自然页面不会重新渲染。

v-model除基础语法糖外的额外处理

大部分开发者对v-model的认知停留在「语法糖,默认等价于传递value prop + 监听对应事件赋值」,但实际上Vue2在编译v-model时做了两层关键的额外处理:

  • 自动适配自定义组件的model配置:自定义组件可以通过model选项修改v-model绑定的prop名和触发事件名,v-model编译时会自动读取这个配置做绑定,不需要手动调整prop和事件名,这也是示例中v-model能正确匹配my-input组件抛出的change事件的原因。
  • 赋值逻辑自动做响应式兜底:v-model编译生成的赋值逻辑不是简单的绑定值 = 新值的裸赋值,内部会先判断目标属性是否已经是响应式属性:如果是给对象新增不存在的属性,会自动调用内部的$set(即Vue.set)逻辑给新属性做响应式劫持,保证赋值后能正常触发视图更新;只有属性本身已经是响应式的时候,才会走直接赋值的逻辑。
    这就是为什么直接给未声明的obj.c赋值不更新,但是用v-model绑定就正常——v-model自动帮你做了$set(obj, 'c', val)的操作。

修复方案

如果要让手动写法和v-model表现完全一致,只需要把赋值逻辑改成用$set实现即可:

<my-input :value="obj.c" @change="(val) => $set(obj, 'c', val)"/>

改完之后两种写法的表现就完全一致了。

补充说明:这个差异在Vue3中已经不存在,Vue3基于Proxy实现响应式,可以天然拦截对象新增属性的操作,不需要$set做额外处理,两种写法在Vue3环境下表现完全等价。


内容的提问来源于stack exchange,提问作者Oscar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 23:39:21