浅析v-model语法糖的实现原理与细节知识及如何让你开发的组件支持v-model
弄明白: v-model
是什么的语法糖? vue2
对原生组件究竟做了什么特殊处理?
弄明白: v-model
到底是单向数据流还是数据双向绑定?
弄明白: v-model
在语法糖之外的『副作用』?
学会如何让你的组件也支持 v-model
语法。
一、v-model 本质是语法糖
在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
1、text 和 textarea 元素使用 value 属性和 input 事件
2、checkbox 和 radio 使用 checked 属性和 change 事件
3、select 字段将 value 作为 prop 并将 change 作为事件
『
v-model
本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。』 -- 官方文档
什么是语法糖?
语法糖,简单来说就是『便捷写法』。
在大部分情况下, v-model="foo"
等价于 :value="foo"
加上 @input="foo = $event"
;
没错,在大部分情况下如此,但也有例外:
(1)vue2
给组件提供了 model
属性,可以让用户自定义传值的prop名和更新值的事件名。这个暂且略过,第四节会细说。
(2)对于原生 html
原生元素,vue
干了大量『脏活儿』,目的是为了能让我们忽视 html
在api上的差异性。以下元素的左右两种写法是等价的:

在编程思想上,这种帮助使用者『隐藏细节』的方式叫封装。
二、v-model 仅仅是语法糖吗?(冷知识)
v-model
不仅仅是语法糖,它还有副作用。
副作用如下:如果 v-model
绑定的是响应式对象上某个不存在的属性,那么 vue
会悄悄地增加这个属性,并让它响应式。举个例子,看下面的代码:
// template中:
<el-input v-model="user.tel"></el-input>
// script中:
export default {
data() {
return {
user: {
name: 'test',
}
}
}
}
响应式数据中没有定义 user.tel
属性,但是 template
里却用 v-model
绑定了 user.tel
,猜一猜当你输入时会发生什么?
揭晓答案吧: user
上会新增 tel
属性,并且 tel
这个属性还是响应式的。这就是『副作用』带来的效果。
三、v-model 是双向绑定?还是单向数据流?
1、v-model 是双向绑定么?
是双向绑定的:『你可以用 v-model 指令在表单 <input>
、<textarea>
及 <select>
元素上创建双向数据绑定。』 —— vue2官方文档
2、那 v-model 是单向数据流吗?
是的,它甚至是单向数据流的典型范式。
虽然官方没有明确表示这点,但我们可以捋一捋两者的关系。
(1)什么是单项数据流?
子组件不能改变父组件传递给它的 prop
属性,推荐的做法是它抛出事件,通知父组件自行改变绑定的值。
(2)v-model
的做法是怎样的?
v-model
做法完全符合单项数据流。甚至于,它给出了一种在命名和事件定义上的规范。
众所周知 .sync
修饰符是单向数据流的另一个典型范式。
『单向数据流』总结起来其实也就8个字:『数据向下,事件向上』。
四、如何让你开发的组件支持 v-model?
虽然不想说,但这确实是高频面试题。
在定义 vue
组件时,你可以提供一个 model
属性,用来定义该组件以何种方式支持 v-model
。
model
属性本身是有默认值的,如下:
// 默认的 model 属性
export default {
model: {
prop: 'value',
event: 'input'
}
}
也就是说,如果你不定义 model
属性,或者你按照当面方法定义属性,当其他人使用你的自定义组件时,v-model="foo"
就完全等价于 :value="foo"
加上 @input="foo = $event"
。
如果把 model
属性进行一些改装,如下:
// 默认的 model 属性
export default {
model: {
prop: 'ame',
event: 'zard'
}
}
那么,v-model="foo"
就等价于 :ame="foo"
加上 @zard="foo = $event"
。
没错,就是这么容易,让我们看个例子。先定义一个自定义组件:
<template>
<div>
我们是TI{{ ame }}冠军
<el-button @click="playDota2(1)">加</el-button>
<el-button @click="playDota2(-1)">减</el-button>
</div>
</template>
<script>
export default {
props: {
ame: {
type: Number,
default: 8
}
},
model: { // 自定义v-model的格式
prop: 'ame', // 代表 v-model 绑定的prop名
event: 'zard' // 代码 v-model 通知父组件更新属性的事件名
},
methods: {
playDota2(step) {
const newYear = this.ame + step
this.$emit('zard', newYear)
}
}
}
</script>
// 然后我们在父组件中使用该组件:
// template中
<dota v-model="ti"></dota>
// script中
export default {
data() {
return {
ti: 8
}
}
}
看具体效果就知道:让你的组件支持 v-model
就这么容易。
文章:《面试官:你真的了解 v-model 吗?(vue2)》 - https://juejin.cn/post/7049135444310622245
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2017-06-26 浅析如何利用逐帧动画配合补间动画实现一个无限循环的文字及图片轮播效果