当前位置: 首页 > Web前端 > vue.js

Web前端培训vue3响应式反应源码分析

时间:2023-03-31 17:48:17 vue.js

1.先看一段基本用法const{reactive,effect}=Vueconsttarget={name:'lixx',age:27}conststate=reactive(target)conststate1=reactive(target)conststate2=reactive(state1)console.log(state===state1,state1===state2)//truetrueeffect(()=>{document.getElementById('app').innerHTML=mine名字是:${state.name},我am${state.age}old})setTimeout(()=>{state.age=28},4000)模块reactive就是将对象转化为响应式模块效果可以理解为依赖跟踪,当执行依赖在函数发生变化时,函数会自动执行。在初始阶段,上面的代码会被执行一次。state===state1,state1===state2是介绍反应式缓存技术2.实现一个简单的手写内容包括:proxy,cache,lazyproxy//源码实现------------------------------------//履行反应式缓存constreactiveMap=newWeakMap()constisObject=(target)=>target&&typeoftarget==='object'constReactiveFlags={IS_REACTIVE:'__v_isReactive'}constreactiveImpl=(target)=>{if(!isObject(target)){console.warn(${target}needisobject)return}//如果传递了源对象就是源对象。源对象被代理后,直接返回代理后的对象。//当目标是代理之前的对象时,这个缓存是constexistingProxy=reactiveMap.get(target)if(existingProxy)returnexistingProxy//当target是这个缓存中的代理对象时if(target&&target[ReactiveFlags.IS_REACTIVE])returntargetconstproxy=newProxy(target,{get:(target,key,receiver)=>{if(key===ReactiveFlags.IS_REACTIVE)returntrueconstres=Reflect.get(target,key,receiver)//如果是对象,继续代理,然后返回返回isObject(res)?reactive(res):res},set:(target,key,value,receiver)=>{Reflect.set(target,key,value,receiver)}})reactiveMap.set(target,proxy)returnproxy}constVue={reactive:reactiveImpl}//使用部分----------------------------------------------------------------const{reactive}=Vueconsttarget={name:'lixx',年龄:27}conststate=reactive(target)conststate1=reactive(target)conststate2=reactive(state1)console.log(state===state1,state1===state2)//truetruedocument.getElementById('app').innerHTML=mine名字是:${state.name},my${state.age}isold以上是reactive的核心部分,但是有几点需要注意:如果你通过如果反应函数的参数是源对象怎么办???如果传递给反应函数的参数是代理后的对象怎么办???如果代理的对象嵌套很深怎么办???根据以上问题,通过我们的手写实现一一解答:Vue使用惰性代理模式解决嵌套问题。Vue3和Vue2的区别之一是Vue2在遇到迭代对象时递归重写。但是Vue3是一个惰性代理_前端训练是通过代码returnisObject(res)实现的吗?反应性(res):res。这里有一个问题:如果让我们去判断对象是否被自己代理了,应该怎么判断呢?使用target&&target[ReactiveFlags.IS_REACTIVE]判断是否被代理。在执行这段代码的时候,如果目标是一个代理对象,那么就会触发proxy.get函数,我们可以使用代码if(key===ReactiveFlags.IS_REACTIVE)returntrue来告诉用户变量已经被代理。一种直观的方式:在代理中添加一个变量,判断该变量是否存在。这种方法是可行的,但是如果我们不想让用户知道有这个变量呢?还有其他的方法,那我们看看Vue是怎么做的。如果传递的对象是代理之前的对象,在源码中会调用Proxy进行代理,将代理后的数据存储在reactiveMap.set(target,proxy)中,如果下次重复代理,则直接返回。可以通过上面的代码体现出来state===state1//true问题1:如果传递给函数reactive的参数是源对象怎么办???问题2:如果传递给函数reactive的参数是proxy之后的对象怎么办???问题3:如果proxy的对象嵌套很深怎么办???3.源码细节扫描3.1reactivecanonlypassobjects//传递的属性必须是对象if(!isObject(target)){if(__DEV__){console.warn(valuecannotbemadereactive:${String(target)})}returntarget}3.2如果已经被代理,直接返回//此时传递的对象是代理后对象//如果已经被代理,直接返回主要是[target[ReactiveFlags.IS_REACTIVE]]worksif(target[ReactiveFlags.RAW]&&!(isReadonly&&target[ReactiveFlags.IS_REACTIVE])){returntarget}//重复代理,为Proxy多次传递同一个对象//判断缓存中是否有值toreturnconstexistingProxy=proxyMap.get(target)if(existingProxy){returnexistingProxy}3.3如果传入的target是数组,单独处理//判断是否是数组consttargetIsArray=isArray(target)if(!isReadonly&&targetIsArray&&hasOwn(arrayInstrumentations,key)){returnReflect.get(arrayInstrumentations,key,receiver)}3.4如果没有读取-only,收集依赖//如果不是只读,触发依赖收集if(!isReadonly){track(target,TrackOpTypes.GET,key)}3.5如果是浅代理,代理一层后直接返回//如果是是shallowproxy,直接returnif(shallow){returnres}3.6如果是ref,直接调用.value进行return//如果proxy的ref直接使用【.value】返回if(isRef(res)){//refunwrapping-skipunwrapforArray+integerkey.returntargetIsArray&&isIntegerKey(key)?res:res.value}3.7如果是深度对象,进行惰性代理//如果返回值对象是惰性代理,则返回if(isObject(res)){returnisReadonly?只读(res):反应式(res)}