记录--妙用computed拦截v-model,面试管都夸我细
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
如何避免写出屎山,优雅的封装组件,在面试官面前大大加分,从这篇文章开始!
保持单向数据流
大家都知道vue是单项数据流的,子组件不能直接修改父组件传过来的props,但是在我们封装组件使用v-model时,不小心就会打破单行数据流的规则,例如下面这样:
<!-- 父组件 -->
<my-component v-model="msg"></my-component> <!-- 子组件 -->
<template>
<div>
<el-input v-model="msg"></el-input>
</div>
</template> <script setup>
defineOptions({
name: "my-component",
}); const props = defineProps({
msg: {
type: String,
default: "",
},
}); </script>
v-model实现原理
直接在子组件上修改props的值,就打破了单向数据流,那我们该怎么做呢,先看下v-model的实现原理:
<!-- 父组件 -->
<template>
<my-component v-model="msg"></my-component>
<!-- 等同于 -->
<my-component :modelValue="msg" @update:modelValue="msg = $event"></my-component>
</template>
emit通知父组件修改prop值
所以,我们可以通过emit,子组件的值变化了,不是直接修改props,而是通知父组件去修改该值!
子组件值修改,触发父组件的update:modelValue事件,并将新的值传过去,父组件将msg更新为新的值,代码如下:
<!-- 父组件 -->
<template>
<my-component v-model="msg"></my-component>
<!-- 等同于 -->
<my-component :modelValue="msg" @update:modelValue="msg = $event"></my-component>
</template>
<script setup>
import { ref } from 'vue'
const msg = ref('hello')
</script> <!-- 子组件 -->
<template>
<el-input :modelValue="modelValue" @update:modelValue="handleValueChange"></el-input>
</template>
<script setup>
const props = defineProps({
modelValue: {
type: String,
default: '',
}
}); const emit = defineEmits(['update:modelValue']); const handleValueChange = (value) => {
// 子组件值修改,触发父组件的update:modelValue事件,并将新的值传过去,父组件将msg更新为新的值
emit('update:modelValue', value)
}
</script>
这也是大多数开发者封装组件修改值的方法,其实还有另一种方案,就是利用计算数据的
get、set
computed 拦截prop
大多数同学使用计算属性,都是用get
,或许有部分同学甚至不知道计算属性还有set
,下面我们看下实现方式吧:
<!-- 父组件 -->
<script setup>
import myComponent from "./components/MyComponent.vue";
import { ref } from "vue"; const msg = ref('hello')
</script> <template>
<div>
<my-component v-model="msg"></my-component>
</div>
</template> <!-- 子组件 -->
<template>
<el-input v-model="msg"></el-input>
</template>
<script setup>
import { computed } from "vue"; const props = defineProps({
modelValue: {
type: String,
default: "",
},
}); const emit = defineEmits(["update:modelValue"]); const msg = computed({
// getter
get() {
return props.modelValue
},
// setter
set(newValue) {
emit('update:modelValue',newValue)
},
});
</script>
v-model绑定对象
那么当v-model绑定的是对象呢?
可以像下面这样,computed拦截多个值
<!-- 父组件 -->
<script setup>
import myComponent from "./components/MyComponent.vue";
import { ref } from "vue"; const form = ref({
name:'张三',
age:18,
sex:'man'
})
</script> <template>
<div>
<my-component v-model="form"></my-component>
</div>
</template> <!-- 子组件 -->
<template>
<div>
<el-input v-model="name"></el-input>
<el-input v-model="age"></el-input>
<el-input v-model="sex"></el-input>
</div>
</template>
<script setup>
import { computed } from "vue"; const props = defineProps({
modelValue: {
type: Object,
default: () => {},
},
}); const emit = defineEmits(["update:modelValue"]); const name = computed({
// getter
get() {
return props.modelValue.name;
},
// setter
set(newValue) {
emit("update:modelValue", {
...props.modelValue,
name: newValue,
});
},
}); const age = computed({
get() {
return props.modelValue.age;
},
set(newValue) {
emit("update:modelValue", {
...props.modelValue,
age: newValue,
});
},
}); const sex = computed({
get() {
return props.modelValue.sex;
},
set(newValue) {
emit("update:modelValue", {
...props.modelValue,
sex: newValue,
});
},
});
</script>
这样是可以实现我们的需求,但是一个个手动拦截v-model对象的属性值,太过于麻烦,假如有10个输入,我们就需要拦截10次,所以我们需要将拦截整合起来!
监听整个对象
<!-- 父组件 -->
<script setup>
import myComponent from "./components/MyComponent.vue";
import { ref } from "vue"; const form = ref({
name:'张三',
age:18,
sex:'man'
})
</script> <template>
<div>
<my-component v-model="form"></my-component>
</div>
</template> <!-- 子组件 -->
<template>
<div>
<el-input v-model="form.name"></el-input>
<el-input v-model="form.age"></el-input>
<el-input v-model="form.sex"></el-input>
</div>
</template>
<script setup>
import { computed } from "vue"; const props = defineProps({
modelValue: {
type: Object,
default: () => {},
},
}); const emit = defineEmits(["update:modelValue"]); const form = computed({
get() {
return props.modelValue;
},
set(newValue) {
alert(123)
emit("update:modelValue", newValue);
},
});
</script>
这样看起来很完美,但是,我们在set中alert(123),它却并未执行!!
原因是:form.xxx = xxx时,并不会触发computed的set,只有form = xxx时,才会触发set
Proxy代理对象
那么,我们需要想一个办法,在form的属性修改时,也能emit("update:modelValue", newValue);
,为了解决这个问题,我们可以通过Proxy代理
<!-- 父组件 -->
<script setup>
import myComponent from "./components/MyComponent.vue";
import { ref, watch } from "vue"; const form = ref({
name: "张三",
age: 18,
sex: "man",
}); watch(form, (newValue) => {
console.log(newValue);
});
</script> <template>
<div>
<my-component v-model="form"></my-component>
</div>
</template> <!-- 子组件 -->
<template>
<div>
<el-input v-model="form.name"></el-input>
<el-input v-model="form.age"></el-input>
<el-input v-model="form.sex"></el-input>
</div>
</template>
<script setup>
import { computed } from "vue"; const props = defineProps({
modelValue: {
type: Object,
default: () => {},
},
}); const emit = defineEmits(["update:modelValue"]); const form = computed({
get() {
return new Proxy(props.modelValue, {
get(target, key) {
return Reflect.get(target, key);
},
set(target, key, value,receiver) {
emit("update:modelValue", {
...target,
[key]: value,
});
return true;
},
});
},
set(newValue) {
emit("update:modelValue", newValue);
},
});
</script>
这样,我们就通过了
Proxy + computed
完美拦截了v-model
的对象!
然后,为了后面使用方便,我们直接将其封装成hook
// useVModel.js
import { computed } from "vue"; export default function useVModle(props, propName, emit) {
return computed({
get() {
return new Proxy(props[propName], {
get(target, key) {
return Reflect.get(target, key)
},
set(target, key, newValue) {
emit('update:' + propName, {
...target,
[key]: newValue
})
return true
}
})
},
set(value) {
emit('update:' + propName, value)
}
})
}
<!-- 子组件使用 -->
<template>
<div>
<el-input v-model="form.name"></el-input>
<el-input v-model="form.age"></el-input>
<el-input v-model="form.sex"></el-input>
</div>
</template>
<script setup>
import useVModel from "../hooks/useVModel"; const props = defineProps({
modelValue: {
type: Object,
default: () => {},
},
}); const emit = defineEmits(["update:modelValue"]); const form = useVModel(props, "modelValue", emit); </script>
本文转载于:
https://juejin.cn/post/7277089907974422588
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。
记录--妙用computed拦截v-model,面试管都夸我细的更多相关文章
- 查询的model里面 一般都要有一个要返回的model做属性 ;查询前要传入得参数,查询后返回的参数 都要集合在一个model中
查询的model里面 一般都要有一个要返回的model做属性
- 实体类,bean文件,pojo文件夹,model文件夹都一样
实体类,bean文件,pojo文件夹,model文件夹都一样,这些都是编写实体类,这是我暂时看到的项目文件
- computed 里面 不能写箭头函数 都要写 function () {} 否则页面会都不显示
computed 里面 不能写箭头函数 都要写 function () {} 否则页面会都不显示
- 工作记录之 [ python请求url ] v s [ java请求url ]
背景: 模拟浏览器访问web,发送https请求url,为了实验需求需要获取ipv4数据包 由于不做后续的内容整理(有内部平台分析),故只要写几行代码请求发送https请求url列表中的url即可 开 ...
- struts2拦截器拦截成功后每次请求都出现拦截时的错误信息
action中验证方法 在执行execute之前执行 @Override public void validate() { // TODO Auto-generated metho ...
- mysql删除重复记录语句,删除除了 id 号不同,其他都相同的学生冗余信息
/** 在Mysql下执行: delete from my.stu where id not in( select min(id) id from my.stu group by code) ; 用途 ...
- react 记录:运行npm run eject命令暴露配置文件都报这个错误
问题: react 使用create-react-app命令创建一个项目,运行npm run eject命令暴露配置文件都报这个错误 原因:主要是脚手架添加 .gitgnore文件,但是却没有本地仓库 ...
- Struts2拦截器的使用 (详解)
Struts2拦截器的使用 (详解) 如何使用struts2拦截器,或者自定义拦截器.特别注意,在使用拦截器的时候,在Action里面必须最后一定要引用struts2自带的拦截器缺省堆栈default ...
- struts拦截器的知识
如何使用struts2拦截器,或者自定义拦截器.特别注意,在使用拦截器的时候,在Action里面必须最后一定要引用struts2自带的拦截器缺省堆栈defaultStack,如下(这里我是引用了str ...
- springboot aop 自定义注解方式实现完善日志记录(完整源码)
版权声明:本文为博主原创文章,欢迎转载,转载请注明作者.原文超链接 一:功能简介 本文主要记录如何使用aop切面的方式来实现日志记录功能. 主要记录的信息有: 操作人,方法名,参数,运行时间,操作类型 ...
随机推荐
- NC17315 背包
题目链接 题目 题目描述 Applese有 \(1\) 个容量为 \(v\) 的背包,有 \(n\) 个物品,每一个物品有一个价值 \(a_i\) ,以及一个大小 \(b_i\) 然后他对此提出了自己 ...
- NC210981 mixup2混乱的奶牛
题目链接 题目 题目描述 混乱的奶牛 [Don Piele, 2007] Farmer John的 N(4 <= N <= 16) 头奶牛中的每一头都有一个唯一的编号 \(S_i (1 & ...
- Java设计模式-备忘录模式Memento
介绍 备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态. 可以这里理解备忘录模式:现实生 ...
- Nacos2作为Dubbo3的配置中心踩坑总结
本文阐述问题所使用的组件版本分别是,Dubbo:3.2.4,Nacos服务端:2.2.3,Nacos客户端:2.2.4. 在Dubbo3的官方文档中,关于如何使用Nacos作为配置中心的详细介绍参考: ...
- Qt实用技巧:Qt从QtCreator更换为VS开发Qt所需要注意的坑
前言 基本都是使用QtCreator开发,使用vs进行一下开发,记录从QtCreator换成VS所遇到的注意的坑. VS装对应的Qt版本助手配置Qt版本 VS装番茄助手 这里 ...
- 网络安全-Linux常用命令
安装上传下载的软件包 yum install lrzsz -y 下载:linux-->windows sz -y /etc/hosts 上传:windows-->linux rz -y 系 ...
- 【Azure 环境】Update-MgEntitlementManagementAccessPackageAssignmentPolicy 命令执行时候遇见的 No HTTP Resource was found 问题分析
Microsoft Graph PowerShell SDK: acts as an API wrapper for the Microsoft Graph APIs, exposing the en ...
- [C++] 进程注入dll版
目录 前言 前提 原理 代码 一些问题 前言 这几天在弄进程注入的事情,一直做不出来直接的进程注入,也就是不要dll的注入.因为dll注入据说容易触发杀软,但是弄了两天没弄出来.代码明明不报错,目标进 ...
- C++ //list容器 构造函数 //list赋值和交换 //list容器大小操作 //list插入和删除,移除 //清空 //list数据存取back(); front() //list 反转和排序
1 //list容器 构造函数 //list赋值和交换 //list容器大小操作 2 //list插入和删除,移除 //清空 //list数据存取back(); front() 3 //list 反转 ...
- 掌握pandas cut函数,一键实现数据分类
pandas中的cut函数可将一维数据按照给定的区间进行分组,并为每个值分配对应的标签.其主要功能是将连续的数值数据转化为离散的分组数据,方便进行分析和统计. 1. 数据准备 下面的示例中使用的数据采 ...