记录--妙用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切面的方式来实现日志记录功能. 主要记录的信息有: 操作人,方法名,参数,运行时间,操作类型 ...
随机推荐
- Working with Dates in PL/SQL(PL/SQL中使用日期)
Working with Dates in PL/SQL By Steven Feuerstein 史蒂芬.佛伊尔斯坦 The previous articles in this introduct ...
- 解决oracle11g ORA-00119 ORA-00132方法
转自:http://blog.sina.com.cn/s/blog_8334b46001016vk5.html 在linux下启动oracle11g是报如下错误: ORA-00119: invalid ...
- virtualbox中给redhat安装增强功能
关于虚拟机中安装redhat请参考其他教程: 1.点击虚拟机菜单:设备--安装增强功能.... 2.ssh连接到redhat,执行以下操作: [root@rhel-server ~]# mount / ...
- 《系列二》-- 8、单例bean的创建
目录 1 源码入口概述 2 getSingleton(beanName, ObjectFactory) 的行为 总结 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需 ...
- Docker实践之10-图形化管理
lazydocker https://github.com/jesseduffield/lazydocker 一个基于命令行终端的,支持Docker和Docker Compose的图形化界面,支持鼠标 ...
- elasticsearch 查询索引和清理索引命令
查询 curl --silent 'http://127.0.0.1:9200/_cat/indices' 删除 curl -X DELETE "localhost:9200/wifiloc ...
- Oracle不走索引的原因
Oracle数据库操作中,为什么有时一个表的某个字段明明有索引,当观察一些语的执行计划确不走索引呢?如何解决呢?本文我们主要就介绍这部分内容,接下来就让我们一起来了解一下 . 不走索引大体有以下几个原 ...
- 【Azure APIM】解决APIM Self-hosted Gateway在AKS上,最开始访问时候遇见的404问题
问题描述 根据APIM官方文档,创建Self-hosted 网关在AKS中( 使用 YAML 将自承载网关部署到 Kubernetes :https://docs.azure.cn/zh-cn/api ...
- 【Azure Kubernetes】通过 kubelogin 进行非交互式登录AKS
问题描述 当对AKS的登录方式(认证和授权)从"Local Account with Kubernetes RBAC "改变为"Azure AD authenticati ...
- 【Azure 应用服务】用App Service部署运行 Vue.js 编写的项目,应该怎么部署运行呢?
问题描述 用App Service部署运行 Vue.js 编写的项目,应该怎么部署运行呢? 问题解答 VUE通常是运行在客户端侧的JS框架. App Service 在这种场景中是以静态文件的形式提供 ...
