聊聊 vue2 与 vue3 的 v-model

从 vue2 聊起

v-model 这个指令主要用于处理双向绑定,对于原生元素可以直接这样写

js 复制代码
<input v-model="message" placeholder="请输入内容" />

当时写起来真是觉得厉害,加一个 v-model 就解决了双向绑定的问题。但随着项目的进展,很快就遇到了需要对组件进行 v-model 的操作,比如使用 ElementUI 的情况下

js 复制代码
<el-input v-model="username" placeholder="请输入用户名"></el-input>

居然也不需要额外的操作,于是那时候形成了一个意识,需要双向绑定时就直接 v-model 即可,但很快就栽了跟头。

有一个需求是需要对 input 做一个封装,于是很自然就写成了

js 复制代码
<my-input v-model="username" placeholder="请输入用户名"></el-input>

<template>
    <div>
        <input placeholder="请输入内容" />
    </div>
<template>

可想而知,v-model 完全没有生效,在这里卡了很久。上网查了相关资料才知道,原来 v-model 没有那么厉害。它的本质实际上只是做了个转化,也就是:

js 复制代码
<input v-model="message" placeholder="请输入内容" /> 
// 相当于
<input :value="message" @input="message = $event" placeholder="请输入内容"/>

由于 <input> 元素自带了 input 事件与 value 属性,所以自然就能监听到。此时进行输入时,会触发 @input,立刻进行赋值,所以 message 立刻进行了更新,也就是双向绑定的效果。

但转头一想,不对啊,为什么 <el-input> 这种组件也能支持呢,回去一看文档,原来这个组件也已经进行了类似的封装。

注:最新的 el-input 已经不再支持 v-model 了,笔者以前是用老版本的

大概就是

js 复制代码
// 父组件 v-model 编译后
<el-input :value="message" @input="message = $event" placeholder="请输入内容"/>

// 子组件
<template>
    <div>
        <input placeholder="请输入内容" :value="value" @input="$emit('input',$event.target.value)" />
    </div>
<template>

不得不提,语法糖虽好,让新手能很简单入手,但也隔开了后面的逻辑,出了 bug 理解成本和修改成本直线上升。 反观 react 的双向绑定就显得通俗易懂了

js 复制代码
// 父组件
<MyInput value={message} onChange={e => setMessage(e.target.value)} />

// 子组件
<input value={value} onChange={onChange} />

再探 vue3

vue2 的写法其实还能理解,拆成编译后的代码比较清晰。但 vue3 就显得拉胯了,看一段示例

js 复制代码
// 父组件
<MyInput v-model="message" />

// 子组件
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />

有的同学可能已经有点迷糊了,这个 modelValue 是哪来的,这个 update:modelValue 又是什么?

老规矩,先看它编译成了什么吧

js 复制代码
<MyInput v-model="message" />

// 编译后
<MyInput :modelValue="message" @update:modelValue="message = $event" />

这下可能就能理解了,v-model 用 modelValue 作为了值,update:modelValue 作为了更新函数,并不关心你的传入参数叫什么,始终保持这个名字。

当然,可以手动进行命名

js 复制代码
<MyInput v-model:message="message" />

// 编译后
<MyInput :message="message" @update:message="message = $event" />

为什么 vue3 会将 v-model 设计成这样呢?其主要优点在于,可以同时兼容多个 v-model

js 复制代码
<MyInput v-model:title="title" v-model:content="content" />

// 编译后
<MyInput :title="title" @update:title="title = $event" :content="content" @update:content="content = $event" />

// 子组件
<template>
  <input :value="title" @input="$emit('update:title', $event.target.value)" placeholder="标题" />
  <input :value="content" @input="$emit('update:content', $event.target.value)" placeholder="内容" />
</template>
相关推荐
拾忆,想起4 小时前
Dubbo本地存根与本地伪装实战指南:提升微服务容错能力的利器
前端·微服务·云原生·架构·dubbo·safari
wuli_滔滔4 小时前
DevUI弹窗体系重构:微前端场景下的模态管理策略
前端·重构·架构
fruge4 小时前
Angular 17 新特性深度解析:独立组件 + 信号系统实战
前端·javascript·vue.js
卓码软件测评4 小时前
第三方软件质量检测机构:【Apifox多格式支持处理JSON、XML、GraphQL等响应类型】
前端·测试工具·正则表达式·测试用例·压力测试
心随雨下4 小时前
Flutter加载自定义CSS样式文件方法
前端·css·flutter
X***C8624 小时前
SpringMVC 请求参数接收
前端·javascript·算法
GDAL4 小时前
css实现元素居中的18种方法
前端·css·面试·html·css3·css居中
copyer_xyf5 小时前
SQL 语法速查手册:前端开发者的学习笔记
前端·数据库·sql
拾忆,想起5 小时前
Dubbo服务版本控制完全指南:实现微服务平滑升级的金钥匙
前端·微服务·云原生·架构·dubbo·safari