聊聊 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>
相关推荐
小小19928 分钟前
idea 配置less转化为css
前端·css·less
hhb_61810 分钟前
Less嵌套避坑:优先级冲突实战解析
前端·css·less
云水一下20 分钟前
Vue.js从零到精通系列(五):全局状态管理——Pinia 核心与实践
前端·javascript·vue.js
我不是外星人28 分钟前
浅谈我对 AI 发展的看法
前端·ai编程·claude
甲维斯1 小时前
测一波Kimi K2.7,消耗一周配额!
前端·人工智能·游戏开发
Dick5071 小时前
ROS2 多机器人通用 Driver 层复盘:BaseRobotDriver 到多平台 Mock 切换实现
前端·javascript·机器人
xiaofeichaichai2 小时前
前端安全 XSS 与 CSRF
前端·安全·xss
JS菌2 小时前
Skills 动态加载系统:让 AI Agent 按需获取领域知识
前端·人工智能·后端
weedsfly2 小时前
Sass 代码复用完全指南:从变量到模块化
前端