持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情
vue3(vue3使用的是Proxy实现监听数据变化)
什么是 Proxy?
Proxy 这个词翻译过来就是“代理”,用在这里表示由它来“代理”某些操作。 Proxy 会在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
先来看下 proxy 的基本语法
const proxy = new Proxy(target, handler)
- target :您要代理的原始对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)
- handler :一个对象,定义将拦截哪些操作以及如何重新定义拦截的操作
我们看一个简单的例子:
const person = {
name: 'muyao',
age: 27
};
const proxyPerson = new Proxy({}, {
get: function(target, propKey) {
return 35;
}
});
proxy.name // 35
proxy.age // 35
proxy.sex // 35 不存在的属性同样起作用
person.name // muyao 原对象未改变
上面代码中,配置对象有一个get方法,用来拦截对目标对象属性的访问请求。get方法的两个参数分别是目标对象和所要访问的属性。可以看到,由于拦截函数总是返回35,所以访问任何属性都得到35
注意,Proxy 并没有改变原有对象 而是生成一个新的对象,要使得 Proxy 起作用,必须针对 Proxy 实例(上例是 proxyPerson)进行操作,而不是针对目标对象(上例是 person)进行操作
Proxy 支持的拦截操作一共 13 种:
- get(target, propKey, receiver) :拦截对象属性的读取,比如
proxy.foo和proxy['foo']。 - set(target, propKey, value, receiver) :拦截对象属性的设置,比如
proxy.foo = v或proxy['foo'] = v, 返回一个布尔值。 - has(target, propKey) :拦截
propKey in proxy的操作,返回一个布尔值。 - deleteProperty(target, propKey) :拦截
delete proxy[propKey]的操作,返回一个布尔值。 - ownKeys(target) :拦截
Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名。 - getOwnPropertyDescriptor(target, propKey) :拦截
Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。 - defineProperty(target, propKey, propDesc) :拦截
Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。 - preventExtensions(target) :拦截
Object.preventExtensions(proxy),返回一个布尔值。 - getPrototypeOf(target) :拦截
Object.getPrototypeOf(proxy),返回一个对象。 - isExtensible(target) :拦截
Object.isExtensible(proxy),返回一个布尔值。 - setPrototypeOf(target, proto) :拦截
Object.setPrototypeOf(proxy, proto),返回一个布尔值。 - apply(target, object, args) :拦截 Proxy 实例作为函数调用的操作,比如
proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。 - construct(target, args) :拦截 Proxy 实例作为构造函数调用的操作,比如
new proxy(...args)。
具体实现:
//data
let state ={
province:'',
city:'',
area:'',
}
//watch方法
let watcher = {
}
for(let key in state){
watcher[key]=(res)=>{
watcher[key+'_callback']=res
}
}
const postParams = new Proxy(state,{
//获取值
get(thisData,key){
return thisData[key]
},
//修改值
set(thisData,key,newValue){
thisData[key]=newValue
//放入watch方法
if(watcher[key+'_callback'])watcher[key+'_callback'](newValue,thisData[key])
}
})
watcher.province(res=>{
console.log(res)
})
postParams.province=1//修改
let state ={
province:'',
city:'',
area:'',
data:{
value:99
}
}
// 设置对象
// valueData监听数据
// watcherValue:监听数据里面存在对象是需要传递watch
// watcherKey:监听数据里面存在对象是需要传递watch的key
// original:最初的数据
function setData(valueData,watcherValue=null,watcherKey=null,original=null){
let watcher = {
}
if(watcherValue){
watcher=watcherValue
}
//watch方法
for(let key in valueData){
// 是否对象
if(valueData[key] instanceof Object){
if(!original){
// 最初对象赋值
original=valueData;
}
let childerObject=null;
// watcher 是否存在
if(watcherValue && watcherKey){
childerObject =setData(valueData[key],watcherValue,watcherKey,original)
}else{
childerObject =setData(valueData[key],watcher,key,original)
}
childerData=childerObject.postParams
childerWatcher=childerObject.watcher
// 当前对象监听
// 在原数据中加入初始值
valueData['_'+key+'_original_']=valueData[key]
// 将当前对象替换成Proxy对象
valueData[key]=childerObject.postParams
// 合并watch
Object.assign(watcher,childerWatcher)
}else{
if(watcherValue && watcherKey){
watcherValue[watcherKey]=(res)=>{
watcher['_'+watcherKey+'_callback']=res
}
}else{
watcher[key]=(res)=>{
watcher['_'+key+'_callback']=res
}
}
}
}
const postParams = new Proxy(valueData,{
//获取值
get(thisData,key){
return thisData[key]
},
//修改值
set(thisData,key,newValue){
//放入watch方法
if(watcherValue && watcherKey){
// 原数据
let oddValueObject=Object.assign({},original[`_${watcherKey}_original_`])
thisData[key]=newValue
// 修改后的
let newValueObject=original[`_${watcherKey}_original_`]
if(watcherValue['_'+watcherKey+'_callback'])watcherValue['_'+watcherKey+'_callback'](newValueObject,oddValueObject)
}else{
let oddValue=thisData[key]
thisData[key]=newValue
if(watcher['_'+key+'_callback'])watcher['_'+key+'_callback'](newValue,oddValue)
}
}
})
return {
postParams,watcher
}
}
//使用
const {postParams,watcher}=setData(state)
watcher.province((newValue,oddValue)=>{
console.log(newValue,oddValue)
})
watcher.data((newValue,oddValue)=>{
console.log(newValue,oddValue)
})
setTimeout(()=>{
postParams.province=9999
postParams.data.value=9999
},1500)
vue2(vue2使用的是defineProperty监听每组数据里面的单个值变化)
具体实现:
//data
const postParams = {
province:'',
city:'',
area:'',
}
//watch方法
let watcher = {
}
for(let key in postParams){
let value = postParams[key]
watcher[key]=(res)=>{
watcher[key+'_callback']=res
}
//监听每一个数据变化
Object.defineProperty(postParams,key,{
get(){
return value
},
set(newValue){
value=newValue
//放入watch
if(watcher[key+'_callback'])watcher[key+'_callback'](newValue,value)
}
})
}
watcher.province(res=>{
console.log(res)
})
postParams.province=1//修改