当前位置:网站首页>浅析v-model语法糖的实现原理与细节知识及如何让你开发的组件支持v-model
浅析v-model语法糖的实现原理与细节知识及如何让你开发的组件支持v-model
2022-07-31 05:20:00 【火兰】
- 弄明白:
v-model是什么的语法糖?vue2对原生组件究竟做了什么特殊处理? - 弄明白:
v-model到底是单向数据流还是数据双向绑定? - 弄明白:
v-model在语法糖之外的『副作用』? - 学会如何让你的组件也支持
v-model语法。
一、v-model 的本质是语法糖。
『
v-model本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。』 -- 官方文档
什么是语法糖?
语法糖,简单来说就是『便捷写法』。
在大部分情况下, v-model="foo" 等价于 :value="foo" 加上 @input="foo = $event";
<!-- 在大部分情况下,以下两种写法是等价的 -->
<el-input v-model="foo" />
<el-input :value="foo" @input="foo = $event" />没错,在大部分情况下如此。
但也有例外:
vue2给组件提供了model属性,可以让用户自定义传值的prop名和更新值的事件名。这个暂且略过,第四节会细说。对于原生
html原生元素,vue干了大量『脏活儿』,目的是为了能让我们忽视html在api上的差异性。以下元素的左右两种写法是等价的:
textarea元素:
select下拉框:
input type='radio'单选框:
input type='checkbox'多选框:
在编程思想上,这种帮助使用者『隐藏细节』的方式叫封装。
二、v-model 仅仅是语法糖吗?(冷知识)
v-model不仅仅是语法糖,它还有副作用。
副作用如下:如果 v-model 绑定的是响应式对象上某个不存在的属性,那么 vue 会悄悄地增加这个属性,并让它响应式。
举个例子,看下面的代码:
// template中:
<el-input v-model="user.tel"></el-input>
// script中:
export default {
data() {
return {
user: {
name: '公众号: 前端要摸鱼',
}
}
}
}响应式数据中没有定义 user.tel 属性,但是 template 里却用 v-model 绑定了 user.tel,猜一猜当你输入时会发生什么?
看效果:
揭晓答案吧: user 上会新增 tel 属性,并且 tel 这个属性还是响应式的。
这就是『副作用』带来的效果,你学会了吗?
三、 v-model 是双向绑定还是单向数据流?
2.1 v-model 是双向绑定吗?
是,官方说是。
『你可以用 v-model 指令在表单 <input>、<textarea> 及 <select> 元素上创建双向数据绑定。』 —— vue2官方文档
2.2 那 v-model 是单向数据流吗?
是的,它甚至是单向数据流的典型范式。
虽然官方没有明确表示这点,但我们可以捋一捋两者的关系。
- 什么是单项数据流?
子组件不能改变父组件传递给它的prop属性,推荐的做法是它抛出事件,通知父组件自行改变绑定的值。 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 就这么容易。
边栏推荐
猜你喜欢
随机推荐
DHCP原理与配置
NFS共享存储服务
简单计算器,单层循环输出乘法表,字符串方法的使用,格式化输出
JDBC的使用
cenos7安装cmake-3.22.2
自动化测试之unittest框架
第一次实践——计算器
FTP服务与配置
青龙面板从零搭建教程
uni-app生命周期
银河麒麟v10 sp1 安装 PostgreSQL 11.16
DOM操作案例1-点击,使表格的颜色切换(点击单元格,整行或整列颜色切换)
PXE高效批量网络装机
ES6-02-let和const关键字
文本三剑客之e`grep,seq文本编辑工具
银河麒麟服务器v10 sp2安装oracle19c
变更管理与 DevOps —— 二者同时进行吗?
常见网络攻击与防御方法
随机数,函数
软链接和硬链接画图,以及代码,一级目录的解释,重定向,创建文件,删除文件,创建目录,删除目录,cp、mv命令的使用













