Vue3之defineModel

随着vue3.4版本的发布,defineModel也正式转正了。它可以简化实现父子组件之间的双向绑定,也是目前官方推荐的双向绑定实现方式。

1.vue3.4的实现双向绑定

大家应该都知道v-model只是一个语法糖,实际就是给组件定义了modelValue属性和监听update:modelValue事件,所以我们以前要实现数据双向绑定需要给子组件定义一个modelValue属性,并且在子组件内要更新modelValue值时需要emit出去一个update:modelValue事件,将新的值作为第二个字段传出去。

举个栗子,父组件的代码如下:

<template><CommonInput v-model="inputValue" />
</template><script setup lang="ts">
import { ref } from "vue";const inputValue = ref();
</script>

 子组件的代码如下:

<template><input:value="props.modelValue"@input="emit('update:modelValue', $event.target.value)"/>
</template><script setup lang="ts">
const props = defineProps(["modelValue"]);
const emit = defineEmits(["update:modelValue"]);
</script>

以前都是这样去实现v-model双向绑定的。但是存在一个问题就是input输入框其实支持直接使用v-model的,我们这里却没有使用v-model而是在input输入框上面添加value属性和input事件。

原因是因为从vue2开始就已经是单向数据流,在子组件中是不能直接修改props中的值。而是应该由子组件中抛出一个事件,由父组件去监听这个事件,然后去修改父组件中传递给props的变量。如果这里我们给input输入框直接加一个v-model="props.modelValue",那么其实是在子组件内直接修改props中的modelValue。由于单向数据流的原因,vue是不支持直接修改props的,所以我们才需要将代码写成上面的样子。

2.defineModel实现数据双向绑定

defineModel是一个宏,所以不需要从vue中import导入,直接使用就可以了。这个宏可以用来声明一个双向绑定 prop,通过父组件的 v-model 来使用。

父组件的代码和前面是一样的,如下:

<template><CommonInput v-model="inputValue" />
</template><script setup lang="ts">
import { ref } from "vue";const inputValue = ref();
</script>

子组件的代码如下:

<template><input v-model="model" />
</template><script setup lang="ts">
const model = defineModel();
model.value = "xxx";
</script>

例子中我们直接将defineModel的返回值使用v-model绑定到input输入框上面,无需定义 modelValue 属性和监听 update:modelValue 事件,代码更加简洁。defineModel的返回值是一个ref,我们可以在子组件中修改model变量的值,并且父组件中的inputValue变量的值也会同步更新,这样就可以实现双向绑定。

那么问题来了,从vue2开始就变成了单向数据流。这里修改子组件的值后,父组件的变量值也被修改了,那这不就变回了vue1的双向数据流了吗?其实并不是这样的,这里还是单向数据流,我们接下来会简单讲一下defineModel的实现原理。

3.实现原理

defineModel其实就是在子组件内定义了一个叫model的ref变量和modelValue的props,并且watch了props中的modelValue。当props中的modelValue的值改变后会同步更新model变量的值。并且当在子组件内改变model变量的值后会抛出update:modelValue事件,父组件收到这个事件后就会更新父组件中对应的变量值。

实现原理代码如下:

<template><input v-model="model" />
</template><script setup lang="ts">
import { ref, watch } from "vue";const props = defineProps(["modelValue"]);
const emit = defineEmits(["update:modelValue"]);
const model = ref();watch(() => props.modelValue,() => {model.value = props.modelValue;}
);
watch(model, () => {emit("update:modelValue", model.value);
});
</script>

上面的代码后你应该了解到了为什么可以在子组件内直接修改defineModel的返回值后父组件对应的变量也会同步更新了吧。我们修改的其实是defineModel返回的ref变量,而不是直接修改props中的modelValue。实现方式还是和vue3.4以前实现双向绑定一样的,只是defineModel这个宏帮我们将以前的那些繁琐的代码给封装到内部实现了。

其实defineModel的源码中是使用 customRef 和 watchSyncEffect 去实现的,这里是为了让大家能够更容易的明白defineModel的实现原理才举的refwatch的例子。

4.defineModel如何定义typedefault

既然defineModel是声明了一个prop,那同样也可以定义prop的typedefault。具体代码如下:

const model = defineModel({ type: String, default: "20" });

除了支持typedefault,也支持requiredvalidator,用法和定义prop时一样。

5.defineModel如何实现多个   v-model   绑定

同样也支持在父组件上面实现多个   v-model   绑定,这时我们给defineModel传的第一个参数就不是对象了,而是一个字符串。

const model1 = defineModel("count1");
const model2 = defineModel("count2");

 在父组件中使用v-model时代码如下:

<CommonInput v-model:count1="inputValue1" />
<CommonInput v-model:count2="inputValue2" />

我们也可以在多个v-model中定义typedefault

const model1 = defineModel("count1", {type: String,default: "aaa",
});

6.defineModel如何使用内置修饰符和自定义修饰符

如果要使用系统内置的修饰符比如trim,父组件的写法还是和之前是一样的:

<CommonInput v-model.trim="inputValue" />

 子组件也无需做任何修改,和上面其他的defineModel例子是一样的:

const model = defineModel();

defineModel也支持自定义修饰符,比如我们要实现一个将输入框的字母全部变成大写的uppercase自定义修饰符,同时也需要使用内置的trim修饰符。

父组件代码如下:

<CommonInput v-model.trim.uppercase="inputValue" />

子组件需要写成下面这样的:

<template><input v-model="modelValue" />
</template><script setup lang="ts">
const [modelValue, modelModifiers] = defineModel({// get我们这里不需要set(value) {if (modelModifiers.uppercase) {return value?.toUpperCase();}},
});
</script>

defineModel传进去的第一个参数就是包含get 和 set 方法的对象,当对modelValue变量进行读操作时会走到get方法里面去,当对modelValue变量进行写操作时会走到set方法里面去。如果只需要对写操作进行拦截,那么可以不用写get

defineModel的返回值也可以解构成两个变量,第一个变量就是我们前面几个例子的ref对象,用于给v-model绑定。第二个变量是一个对象,里面包含了有哪些修饰符,在这里我们有trimuppercase两个修饰符,所以modelModifiers的值为:

{trim: true,uppercase: true
}

在输入框进行输入时,就会走到set方法里面,然后调用value?.toUpperCase()就可以实现将输入的字母变成大写字母。

总结

  • 在子组件内调用defineModel宏会返回一个ref对象,在子组件内可以直接对这个ref对象进行赋值,父组件内的相应变量也会同步修改。
  • defineModel其实就是在子组件内定义了一个ref变量和对应的prop,然后监听了对应的prop保持ref变量的值始终和对应的prop是一样的。在子组件内当修改ref变量值时会抛出一个事件给父组件,让父组件更新对应的变量值,从而实现双向绑定。
  • 使用defineModel({ type: String, default: "20" })就可以定义prop的typedefault等选项。
  • 使用defineModel("count")就可以实现多个v-model绑定。
  • 通过解构 defineModel() 的返回值拿到modelModifiers修饰符对象,配合 get 和 set 转换器选项实现自定义修饰符。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/784410.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux终端命令工具screen入门

screen是linux里可以进行后台保持Terminal交互的工具&#xff0c;日常工作需要多个会话窗口或者恢复窗口时screen很有帮助 screen的功能大体有三个&#xff1a; 会话恢复&#xff1a;**只要Screen本身没有终止&#xff0c;在其内部运行的会话都可以恢复。**这一点对于远程登录的…

电流镜恒流源简介

目录 工作原理 设计要点 应用场景 初步想法&#xff0c;为参加活动先占贴&#xff08;带家人出去玩没时间搞~~&#xff09;&#xff0c;后面优化 电流镜恒流源是一种利用电流镜原理设计的电路&#xff0c;它可以提供恒定的电流输出&#xff0c;不受负载变化或电源电压波动的…

使用Docker搭建SABnzbd

SABnzbd是一款开源的Usenet下载客户端&#xff0c;它能够高效地从Usenet服务器下载NZB文件或者通过NNTP协议下载文章。SABnzbd支持多个同时下载的任务&#xff0c;能够自动处理下载完成后的文件&#xff0c;如解压缩、分类存储等&#xff0c;并且具有Web界面&#xff0c;方便用…

Linux:ip协议

文章目录 ip协议基本认识ip协议的报头 ip协议基本认识 前面对于TCP的内容已经基本结束了&#xff0c;那么这也就意味着在传输层也已经结束了&#xff0c;那么下一步要进入的是的是网络层&#xff0c;网络层中也有很多种协议&#xff0c;这里主要进行解析的是ip协议 前面的TCP…

迅饶科技 X2Modbus 网关 GetUser 信息泄露漏洞复现

0x01 产品简介 X2Modbus是上海迅饶自动化科技有限公司Q开发的一款功能很强大的协议转换网关, 这里的X代表各家不同的通信协议, 2是T0的谐音表示转换, Modbus就是最终支持的标准协议是Modbus协议。用户可以根据现场设备的通信协议进行配置,转成标准的Modbus协议。在PC端仿真…

政安晨:【Keras机器学习实践要点】(十二)—— 迁移学习和微调

目录 设置 介绍 冻结层&#xff1a;了解可训练属性 可训练属性的递归设置 典型的迁移学习工作流程 微调 关于compile()和trainable的重要说明 BatchNormalization层的重要注意事项 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: T…

前端对数据进行分组和计数处理

js对数组数据的处理&#xff0c;添加属性&#xff0c;合并表格数据。 let data[{id:1,group_id:111},{id:2,group_id:111},{id:3,group_id:111},{id:4,group_id:222},{id:5,group_id:222} ]let tempDatadata; tempDatatempData.reduce((arr,item)>{let findarr.find(i>i…

I.MX6ULL的MAC网络外设设备树实现说明二

一. 简介 前面一篇文章说明了 I.MX6ULL的MAC网络外设设备树编写时&#xff0c;必需写的属性与可选的属性信息&#xff0c;以及NXP 官方编写的IMX6ULL的MAC网络外设的设备节点信息&#xff0c;文章如下&#xff1a; I.MX6ULL的MAC网络外设设备树实现说明一-CSDN博客 NXP 官方…

this.$route.back()时的组件缓存

1.this.$route.back()回到上一个路径会重新加载 跳转时,前一个路由的内容会被销毁,当回来时,重新创建树,组件内有保存了距离,没有一开始是0. 2.keep-alive写在router-view上面,这个地方所代表的路由会被保存,因此可以写在上面,保存,当返回时,如果是这个路由,里面的内容是一样…

CommunityToolkit.Mvvm----配置

一、介绍&#xff1a; CommunityToolkit.Mvvm 包&#xff08;又名 MVVM 工具包&#xff0c;以前称为 Microsoft.Toolkit.Mvvm&#xff09;是一个现代、快速和模块化的 MVVM 库。 它是 .NET 社区工具包的一部分&#xff0c;围绕以下原则生成&#xff1a; 独立于平台和运行时 - …

C语言八大排序(个人笔记)

八大排序 插入排序希尔排序选择排序堆排序冒泡排序快速排序归并排序计算排序 插入排序 基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为止&#xff0c;得到一个新的有序序列 。 直接插入排序的特性总结&am…

Android进阶学习:移动端开发重点学习的十点,不能再得过且过的写业务代码了

最近有朋友问我&#xff1a;“安卓开发是不是没人要了&#xff0c;除了画 UI 别的都不会怎么办&#xff1f;” 考虑到这可能是很多人共同的疑问&#xff0c;决定简单写一下。 说了很多遍了&#xff0c;**不是安卓开发没人要了&#xff0c;是初级安卓没人要了。**现在还在大量…

Karrier One在Sui上构建无线电话服务

Karrier One计划实现无线连接的民主化&#xff0c;为长期以来一直缺乏稳定服务或根本没有服务的地区提供服务&#xff0c;并为没有传统银行账户的人提供现代支付能力。 但是&#xff0c;将以行动迟缓著称的电信行业引入Web3世界是一项艰巨的任务。Karrier One团队决定利用Sui技…

保研线性代数机器学习基础复习2

1.什么是群&#xff08;Group&#xff09;&#xff1f; 对于一个集合 G 以及集合上的操作 &#xff0c;如果G G-> G&#xff0c;那么称&#xff08;G&#xff0c;&#xff09;为一个群&#xff0c;并且满足如下性质&#xff1a; 封闭性&#xff1a;结合性&#xff1a;中性…

【WPF应用24】C#中的Image控件详解与应用示例

在C#应用程序开发中&#xff0c;图像显示是一个常见的需求。无论是创建图形界面还是处理图像数据&#xff0c;System.Windows.Controls.Image控件都是实现这一目标的重要工具。本文将详细介绍Image控件的功能、用法、优化技巧以及一些实际应用示例&#xff0c;帮助开发者更好地…

超强命令行解析工具Apache Commons CLI

概述 为什么要写这篇文章呢?因为在读flink cdc3.0源码的时候发现了这个工具包,感觉很牛,之前写过shell命令,shell是用getopts来处理命令行参数的,但是其实写起来很麻烦,长时间不写已经完全忘记了,现在才发现原来java也有这种工具类,所以先学习一下这个的使用,也许之后自己在写…

速通汇编(三)寄存器及汇编mul、div指令

一&#xff0c;寄存器及标志 AH&ALAX(accumulator)&#xff1a;累加寄存器BH&BLBX(base)&#xff1a;基址寄存器CH&CLCX(count)&#xff1a;计数寄存器DH&DLDX(data)&#xff1a;数据寄存器SP(Stack Pointer)&#xff1a;堆栈指针寄存器BP(Base Pointer)&#…

红黑树是什么,为什么HashMap使用红黑树代替数组+链表?

前言 我们都知道在HashMap中&#xff0c;当数组长度大于64并且链表长度大于8时&#xff0c;HashMap会从数组链表的结构转换成红黑树&#xff0c;那为什么要转换成红黑树呢&#xff0c;或者为什么不一开始就使用红黑树呢&#xff1f;接下来我们将去具体的去剖析一下&#xff01;…

java计算机网络(一)-- url,tcp,udp,socket

网络编程&#xff1a; 计算机网络 计算机网络指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络操作系统、网络管理软件及网络通信协议的管理和协调下&#xff0c;实现资源共享和信息传递的计算机系统。 网络协议…

面试碰到的一些问题

总结一下前两天面试没回答好的问题&#xff0c;如有错误&#xff0c;请各位大佬指正&#xff1a; 1. openfeign的底层实现原理&#xff1f; 2.状态机怎么使用&#xff1f;能解决什么问题 1、openfeign的底层实现原理 OpenFeign是一个基于Java的HTTP客户端框架&#xff0c;底层…