> 文章列表 > 在Vue2项目中使用new Proxy做一些事情。

在Vue2项目中使用new Proxy做一些事情。

在Vue2项目中使用new Proxy做一些事情。

随着Vue3的全面普及,new Proxy成为了无法绕过的话题,更先进的new Proxy为整个Vue3的响应系统提供了动力。

一、前言:

在日常项目开发过程中,我们通常会去”监听“一些值的变化,并通过数据的变化去做某些判断,某些更改等。

二、监听数据变化

1、事件绑定

<el-select v-model="selectValue"@change="selectValueChange"><el-optionv-for="items in rejectSubmitFunctionCodeOptions":key="items.value":label="items.label":value="items.value"/>
</el-select>
selectValueChange () {// TODO: 根据最新的值,或者变化的这个动作做一些事情  
},

2、侦听器

watch: {selectValue (newVal, oldVal) {// TODO: 根据最新的值、旧值或者变化的这个动作做一些事情console.log(newVal, oldVal)},
},

分析

第一种方式:事件绑定的方式,我们虽然能获取到最新的值,以及变化的这个动作,但是我们并不能获取到改变之前的值。此种方式更适用于监听某一个表单项的变化,然后去做一些事情。

第二种方式:通过watch监听,可以获取到newVal和oldVal,但是如果被监听的数据是一个对象,我们并不能直接获取到是对象中具体的哪个属性发生了变化。此种方式更适用于监听某个对象的变化,且可以利用旧值,然后去做一些事情。

某些情况下,我们可能需要监听整个表单中所有表单项的变化,并根据不同的表单项变化做不同的事儿,这个表单可能由几项组成,也可能由几十项、几百项组成。此时如果采用事件绑定的方式无疑代码会异常冗余,难以维护。如果采用watch去监听整个对象,又无法获取到每次变化的具体key是什么。此时就需要Proxy来协助我们了。

三、认识Proxy 

一个 Proxy 对象由两个部分组成: target 、 handler 。在通过 Proxy 构造函数生成实例对象时,需要提供这两个参数。 target 即目标对象, handler 是一个对象,声明了代理 target 的指定行为。

handler的属性:

get(target, propKey, receiver):用于 target 对象上 propKey 的读取操作。

set(target, propKey, value, receiver):用于拦截 target 对象上的 propKey 的赋值操作。如果目标对象自身的某个属性,不可写且不可配置,那么set方法将不起作用。

apply(target, ctx, args):用于拦截函数的调用、call 和 reply 操作。target 表示目标对象,ctx 表示目标对象上下文,args 表示目标对象的参数数组。

has(target, propKey):用于拦截 HasProperty 操作,即在判断 target 对象是否存在 propKey 属性时,会被这个方法拦截。此方法不判断一个属性是对象自身的属性,还是继承的属性

construct(target, args):用于拦截 new 命令。返回值必须为对象。

deleteProperty(target, propKey):用于拦截 delete 操作,如果这个方法抛出错误或者返回 false ,propKey 属性就无法被 delete 命令删除。

getOwnPropertyDescriptor(target, propKey):用于拦截 Object.getOwnPropertyD() 返回值为属性描述对象或者 undefined 。

getPrototypeOf(target):主要用于拦截获取对象原型的操作。返回值必须是对象或者 null ,否则报错。另外,如果目标对象不可扩展(non-extensible),getPrototypeOf 方法必须返回目标对象的原型对象。

isExtensible(target):用于拦截 Object.isExtensible 操作。该方法只能返回布尔值,否则返回值会被自动转为布尔值。它的返回值必须与目标对象的isExtensible属性保持一致,否则会抛出错误。

ownKeys(target):用于拦截对象自身属性的读取操作,方法返回的数组成员,只能是字符串或 Symbol 值,否则会报错。若目标对象中含有不可配置的属性,则必须将这些属性在结果中返回,否则就会报错。若目标对象不可扩展,则必须全部返回且只能返回目标对象包含的所有属性,不能包含不存在的属性,否则也会报错。

preventExtensions(target):拦截 Object.preventExtensions 操作,该方法必须返回一个布尔值,否则会自动转为布尔值。

setPrototypeOf:主要用来拦截 Object.setPrototypeOf 方法。返回值必须为布尔值,否则会被自动转为布尔值。若目标对象不可扩展,setPrototypeOf 方法不得改变目标对象的原型。

Proxy.revocable():用于返回一个可取消的 Proxy 实例。

四、利用Proxy监听数据变化

1、使用背景

目前开发的项目中有一个复杂的审批意见表单组件,其中包含多种联动赋值及联动显示隐藏的规则,并且在业务层也会根据表单的变化做一些定制化的操作,并且某些定制化内容需要用到变化之前的值即oldVal。因此我需要将变化的表单项的key、oldVal、newVal通知给业务层,并且组件内部也根据这些值做一些判断。

2、代码

<template><div><!-- TODO: 这里是表单内容,此处省略--></div>
</template><script>export default {name: 'ExamineApproveOpinionPackaged',props: {// TODO: ...省略// 接收来自业务层的方法:获取组件内值的变化getFormDataChange: {type: Function,default: (key, oldVal, val) => {console.log(key, oldVal, val);}},},data () {return {// 表单数据formData: {conditions: '1', //是否content: '', // 意见whetherAssign: '', // 是否指定驳回节点rejectFunctionCode: '', // 指定驳回节点functioncoderejectAssigneeId: '', // 指定驳回节点用户idrejectSubmitFunctionCode: '', // 指定提交节点的functionCoderejectSubmitAssigneeId: '', // 指定提交节点的人员IDfileList: [], // 文件列表rejectAssigneeIdValue: '', // 指定下级审查人员},// TODO: ...省略};},mounted () {// TODO: 在某个表单项被隐藏时,应该清空被隐藏表单项的key对应的formData的key的值// TODO: 应该监听到表单中每个key的变化const that = this;const interceptors = {set (obj, key, value) {// TODO: 将更新的内容通知到业务层, 值不同的情况下if (value !== obj[key]) {that.getFormDataChange(key, obj[key], value);that.formChange2Data(key, obj[key], value);}obj[key] = value;return true;}// get () {//     ....// }};// TODO: 代理需要监听的数据this.formData = new Proxy(this.formData, interceptors);},methods: {// 选择的值不同,要去清空某些值formChange2Data (key, oldVal, newVal) {// TODO: ...省略},}};
</script>

五、参考文章 

3.1.2 ES6 Reflect 与 Proxy | 菜鸟教程