vue3中的reactive()怎么使用


本篇内容介绍了“vue3中的reactive()怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! 调试版本为3.2.45什么是reactive?reactive是Vue3中提供实现响应式数据的方法.在Vue2中响应式数据是通过defineProperty来实现的.而在Vue3响应式数据是通过ES6的Proxy来实现的reactive注意点reactive参数必须是对象(json/arr)如果给reactive传递了其他对象,默认情况下修改对象,界面不会自动更新,如果想更新,可以通过重新赋值的方式。

接下来我们可以开始调试了,设置好断点后,只要重新刷新页面就可以进入调试界面。我们先调试简单的基本数据类型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函数调用的其中两个参数mutableHandlersmutableCollectionHandlers开始往上查询

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是数组,需要对一些方法(针对includesindexOflastIndexOfpushpopshiftunshiftsplice)进行特殊处理。并对数组的每个元素执行收集依赖,然后通过Reflect获取数组函数的值。Reflect获取值。判断是否为特殊的属性值,symbol, __proto____v_isRef__isVue, 如果是直接返回前面得到的res,不做后续处理;如果为ref对象,target不是数组的情况下,会自动解包。如果resObject,进行深层响应式处理。从这里就能看出,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.触发依赖,这里太过复杂,笔者也没搞懂,如果有兴趣的读者可自行去调试

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对象,被reactiveapi处理过就会有__v_raw私有属性,然后再进行receiver的判断,判断target是否为只读或浅层响应。如果都不是则从缓存proxy的WeakMap对象中获取该元素。最后直接返回target的原始数据(未被proxy代理过)。最后回到之前的判断,由下图可知,target__v_raw属性存在,isReadonly为false,__v_isReactive的值为true,可以说明reactive函数需要处理的对象是一个被reactiveAPI处理过的对象,然后直接返回该对象的原始数据。经过ref函数处理,其本质也是一个对象,所以使用reactive函数处理ref类型就跟处理复杂数据类型一样过程。(开发中应该不会有这种嵌套行为吧,这里只是为了测试多样化)。


Map 类型是键值对的有序列表,而键和值都可以是任意类型。SetMap类似,也是一组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);的三元表达式中可得知,这里的handlercollectionHandlers。网上查找可在reactive函数中return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);这条语句找到,当rawType=1handler是用mutableHandlers,rawType=1时是用mutableCollectionHandlersmutableCollectionHandlers方法:

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应用程序中怎么使用模型工厂”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。Laravel 模型工厂是你可以在…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 03/06 17:59
下一篇 03/06 17:59

相关推荐