本篇内容介绍了“vue3中的reactive()怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! 调试版本为3.2.45
什么是reactive?reactive是Vue3中提供实现响应式数据的方法.在Vue2中响应式数据是通过defineProperty来实现的.而在Vue3响应式数据是通过ES6的Proxy来实现的reactive注意点reactive参数必须是对象(json/arr)如果给reactive传递了其他对象,默认情况下修改对象,界面不会自动更新,如果想更新,可以通过重新赋值的方式。
{{data.name}}
接下来我们可以开始调试了,设置好断点后,只要重新刷新页面就可以进入调试界面。我们先调试简单的基本数据类型1.
/*1.初始进来函数,判断目标对象target是否为只读对象,如果是直接返回*/ functionreactive(target){ //iftryingtoobserveareadonlyproxy,returnthereadonlyversion. if(isReadonly(target)){ returntarget; } //创建一个reactive对象,五个参数后续会讲解 returncreateReactiveObject(target,false,mutableHandlers,mutableCollectionHandlers,reactiveMap); } /*2.判断是来判断target是否为只读。*/ functionisReadonly(value){ return!!(value&&value["__v_isReadonly"/*ReactiveFlags.IS_READONLY*/]); } /*3.创建一个reactive对象*/ /*createReactiveObject接收五个参数: target被代理的对象, isReadonl是不是只读的, baseHandlersproxy的捕获器, collectionHandlers针对集合的proxy捕获器, proxyMap一个用于缓存proxy的`WeakMap`对象*/ functioncreateReactiveObject(target,isReadonly,baseHandlers,collectionHandlers,proxyMap){ //如果target不是对象则提示并返回 /*这里会跳转到如下方法 判断是否原始值是否为object类型 constisObject=(val)=>val!==null&&typeofval==='object'; */ if(!isObject(target)){ if((process.env.NODE_ENV!=='production')){ console.warn(`valuecannotbemadereactive:${String(target)}`); } returntarget; } //如果target已经是proxy是代理对象则直接返回. if(target["__v_raw"/*ReactiveFlags.RAW*/]&& !(isReadonly&&target["__v_isReactive"/*ReactiveFlags.IS_REACTIVE*/])){ returntarget; } //从proxyMap中获取缓存的proxy对象,如果存在的话,直接返回proxyMap中对应的proxy。否则创建proxy。 constexistingProxy=proxyMap.get(target); if(existingProxy){ returnexistingProxy; } //并不是任何对象都可以被proxy所代理。这里会通过getTargetType方法来进行判断。 consttargetType=getTargetType(target); //当类型值判断出是不能代理的类型则直接返回 if(targetType===0/*TargetType.INVALID*/){ returntarget; } //通过使用Proxy函数劫持target对象,返回的结果即为响应式对象了。这里的处理函数会根据target对象不同而不同(这两个函数都是参数传入的): //Object或者Array的处理函数是collectionHandlers; //Map,Set,WeakMap,WeakSet的处理函数是baseHandlers; constproxy=newProxy(target,targetType===2/*TargetType.COLLECTION*/?collectionHandlers:baseHandlers); proxyMap.set(target,proxy); returnproxy; }
getTargetType
方法调用流程
//1.进入判断如果value有__v_skip属性且为true或对象是可拓展则返回0,否则走类型判断函数 functiongetTargetType(value){ //Object.isExtensible()方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。 returnvalue["__v_skip"/*ReactiveFlags.SKIP*/]||!Object.isExtensible(value) ?0/*TargetType.INVALID*/ :targetTypeMap(toRawType(value)); } //2.这里通过Object.prototype.toString.call(obj)来判断数据类型 consttoRawType=(value)=>{ //extract"RawType"fromstringslike"[objectRawType]" returntoTypeString(value).slice(8,-1); }; consttoTypeString=(value)=>objectToString.call(value); //3.这里rawType是为'Object'所以会返回1 functiontargetTypeMap(rawType){ switch(rawType){ case'Object': case'Array': return1/*TargetType.COMMON*/; case'Map': case'Set': case'WeakMap': case'WeakSet': return2/*TargetType.COLLECTION*/; default: return0/*TargetType.INVALID*/;//返回0说明除前面的类型外其他都不能被代理,如Date,RegExp,Promise等 } }
在createReactiveObject
方法中const proxy = new Proxy(target, targetType === 2 /* TargetType.COLLECTION */ ? collectionHandlers : baseHandlers);
这一条语句中,第二个参数判断target是否为Map或者Set类型。从而使用不同的handler来进行依赖收集。在调试的文件node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js
中,我们从reactive
函数的createReactiveObject
函数调用的其中两个参数mutableHandlers
和mutableCollectionHandlers
开始往上查询
constmutableHandlers={ get,//获取值的拦截,访问对象时会触发 set,//更新值的拦截,设置对象属性会触发 deleteProperty,//删除拦截,删除对象属性会触发 has,//绑定访问对象时会拦截,in操作符会触发 ownKeys//获取属性key列表 }; functiondeleteProperty(target,key){ //key是否是target自身的属性 consthadKey=hasOwn(target,key); //旧值 constoldValue=target[key]; //调用Reflect.deleteProperty从target上删除属性 constresult=Reflect.deleteProperty(target,key); //如果删除成功并且target自身有key,则触发依赖 if(result&&hadKey){ trigger(target,"delete"/*TriggerOpTypes.DELETE*/,key,undefined,oldValue); } returnresult; } // functionhas(target,key){ //检查目标对象是否存在此属性。 constresult=Reflect.has(target,key); //key不是symbol类型或不是symbol的内置属性,进行依赖收集 if(!isSymbol(key)||!builtInSymbols.has(key)){ track(target,"has"/*TrackOpTypes.HAS*/,key); } returnresult; } /*ownKeys可以拦截以下操作: 1.Object.keys() 2.Object.getOwnPropertyNames() 3.Object.getOwnPropertySymbols() 4.Reflect.ownKeys()操作*/ functionownKeys(target){ track(target,"iterate"/*TrackOpTypes.ITERATE*/,isArray(target)?'length':ITERATE_KEY); returnReflect.ownKeys(target); }
constget=/*#__PURE__*/createGetter(); /*传递两个参数默认都为false isReadonly是否为只读 shallow是否转换为浅层响应,即Reactive--->shallowReactive,shallowReactive监听了第一层属性的值,一旦发生改变,则更新视图;其他层,虽然值发生了改变,但是视图不会免费云主机域名进行更新 */ functioncreateGetter(isReadonly=false,shallow=false){ returnfunctionget(target,key,receiver){ //1.是否已被reactive相关api处理过; if(key==="__v_isReactive"/*ReactiveFlags.IS_REACTIVE*/){ return!isReadonly; } //2.是否被readonly相关api处理过 elseif(key==="__v_isReadonly"/*ReactiveFlags.IS_READONLY*/){ returnisReadonly; } elseif(key==="__v_isShallow"/*ReactiveFlags.IS_SHALLOW*/){ returnshallow; } //3.检测__v_raw属性 elseif(key==="__v_raw"/*ReactiveFlags.RAW*/&& receiver=== (isReadonly ?shallow ?shallowReadonlyMap :readonlyMap :shallow ?shallowReactiveMap :reactiveMap).get(target)){ returntarget; } //4.如果target是数组,且命中了一些属性,则执行函数方法 consttargetIsArray=isArray(target); if(!isReadonly&&targetIsArray&&hasOwn(arrayInstrumentations,key)){ returnReflect.get(arrayInstrumentations,key,receiver); } //5.Reflect获取值 constres=Reflect.get(target,key,receiver); //6.判断是否为特殊的属性值 if(isSymbol(key)?builtInSymbols.has(key):isNonTrackableKeys(key)){ returnres; } if(!isReadonly){ track(target,"get"/*TrackOpTypes.GET*/,key); } if(shallow){ returnres; } //7.判断是否为ref对象 if(isRef(res)){ //refunwrapping-skipunwrapforArray+integerkey. returntargetIsArray&&isIntegerKey(key)?res:res.value; } //8.判断是否为对象 if(isObject(res)){ //Convertreturnedvalueintoaproxyaswell.wedotheisObjectcheck //heretoavoidinvalidvaluewarning.Alsoneedtolazyaccessreadonly //andreactiveheretoavoidcirculardependency. returnisReadonly?readonly(res):reactive(res); } returnres; }; }
检测__v_isReactive
属性,如果为true,表示target已经是一个响应式对象了。依次检测__v_isReadonly
和__v_isShallow
属性,判断是否为只读和浅层响应,如果是则返回对应包装过的target。检测__v_raw
属性,这里是三元的嵌套,主要判断原始数据是否为只读或者浅层响应,然后在对应的Map里面寻找是否有该目标对象,如果都为true则说明target已经为响应式对象。如果target是数组,需要对一些方法(针对includes
、indexOf
、lastIndexOf
、push
、pop
、shift
、unshift
、splice
)进行特殊处理。并对数组的每个元素执行收集依赖,然后通过Reflect获取数组函数的值。Reflect
获取值。判断是否为特殊的属性值,symbol
, __proto__
,__v_isRef
,__isVue
, 如果是直接返回前面得到的res
,不做后续处理;如果为ref
对象,target
不是数组的情况下,会自动解包。如果res
是Object
,进行深层响应式处理。从这里就能看出,Proxy
是懒惰式的创建响应式对象,只有访问对应的key
,才会继续创建响应式对象,否则不用创建。例子:data.name='2'
constset=/*#__PURE__*/createSetter(); //shallow是否转换为浅层响应,默认为false functioncreateSetter(shallow=false){ //1.传递四个参数 returnfunctionset(target,key,value,receiver){ letoldValue=target[key]; //首先获取旧值,如果旧值是ref类型,且新值不是ref类型,则不允许修改 if(isReadonly(oldValue)&&isRef(oldValue)&&!isRef(value)){ returnfalse; } //2.根据传递的shallow参数,来执行之后的操作 if(!shallow){ if(!isShallow(value)&&!isReadonly(value)){ oldValue=toRaw(oldValue); value=toRaw(value); } if(!isArray(target)&&isRef(oldValue)&&!isRef(value)){ oldValue.value=value; returntrue; } } //3.检测key是不是target本身的属性 consthadKey=isArray(target)&&isIntegerKey(key) ?Number(key)
1、以data.name='2'
这段代码为例,四个参数分别为:target
:目标对象,即target={"name": "测试","age": 10}
(此处为普通对象)key
:修改的对应key,即key: "name"
value
:修改的值,即value: "2"
receiver
:目标对象的代理。即receiver=Proxy {"name": "测试","age": 10}
2、shallow
为false的时候。第一个判断:如果新值不是浅层响应式并且不是readonly,新旧值取其对应的原始值。第二个判断:如果target不是数组并且旧值是ref类型,新值不是ref类型,直接修改oldValue.value为value3.检测key
是不是target本身的属性。这里的hadKey
有两个方法,isArray
就不解释,就是判断是否为数组isIntegerKey
:判断是不是数字型的字符串key值
//判断参数是否为string类型,是则返回true constisString=(val)=>typeofval==='string'; //如果参数是string类型并且不是'NaN',且排除-值(排除负数),然后将key转换成数字再隐式转换为字符串,与原key对比 constisIntegerKey=(key)=>isString(key)&& key!=='NaN'&& key[0]!=='-'&& ''+parseInt(key,10)===key;
4.比较新旧值,如果新旧值不同,则触发依赖进行更新hasChanged
方法
//Object.is()方法判断两个值是否是相同的值。 consthasChanged=(value,oldValue)=>!Object.is(value,oldValue);
5.触发依赖,这里太过复杂,笔者也没搞懂,如果有兴趣的读者可自行去调试
{{data.name}}
Click
const num = reactive(2)
这里比较简单,在createReactiveObject
函数方法里面:
if(!isObject(target)){ if((process.env.NODE_ENV!=='production')){ console.warn(`valuecannotbemadereactive:${String(target)}`); } returntarget; }
因为判断类型不是对象,所以会在控制台打印出警告,并且直接返回原数据
1.调试开始进来reactive
函数,然后会经过isReadonly
函数,这里跟前面不同的是,target是一个proxy对象,它已经被代理过有set
,get
等handler。所以在isReadonly
函数读取target
的时候,target
会进行get
函数的读取操作。
functionreactive(target){ //iftryingtoobserveareadonlyproxy,returnthereadonlyversion. if(isReadonly(target)){ returntarget; } returncreateReactiveObject(target,false,mutableHandlers,mutableCollectionHandlers,reactiveMap); }
2.可以看到get
传入的参数有个key="__v_isReadonly"
,这里的isReadonly
返回是false,接下来进入createReactiveObject
函数这里说明下,在本次调试中常见的vue里面定义的私有属性有:__v_skip
:是否无效标识,用于跳过监听__v_isReactive
:是否已被reactive相关api处理过__v_isReadonly
:是否被readonly相关api处理过__v_isShallow
:是否为浅层响应式对象__v_raw
:当前代理对象的源对象,即target3.在createReactiveObject
函数中,经过target["__v_isReactive"]
的时候会触发target
的get函数,这时候get
函数传入的参数中key='__v_raw'
if(target["__v_raw"/*ReactiveFlags.RAW*/]&& !(isReadonly&&target["__v_isReactive"/*ReactiveFlags.IS_REACTIVE*/])){ returntarget; }
由上图可知我们检测target
即已定义过的proxy对象,被reactive
api处理过就会有__v_raw
私有属性,然后再进行receiver
的判断,判断target
是否为只读或浅层响应。如果都不是则从缓存proxy的WeakMap
对象中获取该元素。最后直接返回target
的原始数据(未被proxy代理过)。最后回到之前的判断,由下图可知,target
的__v_raw
属性存在,isReadonly
为false,__v_isReactive
的值为true,可以说明reactive
函数需要处理的对象是一个被reactive
API处理过的对象,然后直接返回该对象的原始数据。经过ref
函数处理,其本质也是一个对象,所以使用reactive
函数处理ref
类型就跟处理复杂数据类型一样过程。(开发中应该不会有这种嵌套行为吧,这里只是为了测试多样化)。
Map
类型是键值对的有序列表,而键和值都可以是任意类型。Set
和Map
类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set
中,没有重复的key。
由上图可知Map结构和Set结构使用typeof
判断是object
,所有流程前面会跟复杂数据类型一样,知道在createReactiveObject
函数的getTargetType()
函数开始不同。在getTargetType
函数里面toRawType()
判断数据类型所用方法为Object.prototype.toString.call()
consttargetType=getTargetType(target); functiongetTargetType(value){ returnvalue["__v_skip"/*ReactiveFlags.SKIP*/]||!Object.isExtensible(value) ?0/*TargetType.INVALID*/ :targetTypeMap(toRawType(value)); } functiontargetTypeMap(rawType){//rawType="Map",这里返回值为2 switch(rawType){ case'Object': case'Array': return1/*TargetType.COMMON*/; case'Map': case'Set': case'WeakMap': case'WeakSet': return2/*TargetType.COLLECTION*/; default: return0/*TargetType.INVALID*/; } }
这时候targetType=2
,在createReactiveObject
的函数中const proxy = new Proxy(target, targetType === 2 /* TargetType.COLLECTION */ ? collectionHandlers : baseHandlers);
的三元表达式中可得知,这里的handler
为collectionHandlers
。网上查找可在reactive
函数中return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);
这条语句找到,当rawType=1
时handler
是用mutableHandlers
,rawType=1
时是用mutableCollectionHandlers
。mutableCollectionHandlers
方法:
constmutableCollectionHandlers={ get:/*#__PURE__*/createInstrumentationGetter(false,false) }; //解构createInstrumentations const[mutableInstrumentations,readonlyInstrumentations,shallowInstrumentations,shallowReadonlyInstrumentations]=/*#__PURE__*/createInstrumentations(); //传入两个参数,是否为可读,是否为浅层响应 functioncreateInstrumentationGetter(isReadonly,shallow){ constinstrumentations=shallow ?isReadonly ?shallowReadonlyInstrumentations :shallowInstrumentations :isReadonly ?readonlyInstrumentations :mutableInstrumentations; return(target,key,receiver)=>{ if(key==="__v_isReactive"/*ReactiveFlags.IS_REACTIVE*/){ return!isReadonly; } elseif(key==="__v_isReadonly"/*ReactiveFlags.IS_READONLY*/){ returnisReadonly; } elseif(key==="__v_raw"/*ReactiveFlags.RAW*/){ returntarget; } returnReflect.get(hasOwn(instrumentations,key)&&keyintarget ?instrumentations :target,key,receiver); }; }
//篇幅问题以及这方面笔者并未深入,所以就大概带过 functioncreateInstrumentations(){ //创建了四个对象,对象内部有很多方法,其他去掉了,完整可自行去调试查看 constmutableInstrumentations={ get(key){ returnget$1(this,key); }, getsize(){ returnsize(this); }, has:has$1, add, set:set$1, delete:deleteEntry, clear, forEach:createForEach(false,false) }; ................. //通过createIterableMethod方法操作keys、values、entries、Symbol.iterator迭代器方法 constiteratorMethods=['keys','values','entries',Symbol.iterator]; iteratorMethods.forEach(method=>{ mutableInstrumentations[method]=createIterableMethod(method,false,false); readonlyInstrumentations[method]=createIterableMethod(method,true,false); shallowInstrumentations[method]=createIterableMethod(method,false,true); shallowReadonlyInstrumentations[method]=createIterableMethod(method,true,true); }); return[ mutableInstrumentations, readonlyInstrumentations, shallowInstrumentations, shallowReadonlyInstrumentations ]; }
“vue3中的reactive()怎么使用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注百云主机网站,小编将为大家输出更多高质量的实用文章!
本文小编为大家详细介绍“Laravel应用程序中怎么使用模型工厂”,内容详细,步骤清晰,细节处理妥当,希望这篇“Laravel应用程序中怎么使用模型工厂”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。Laravel 模型工厂是你可以在…
免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。