【vue3源码】9.ref源码分析参考代码版本:vue3.2.37官方文档:https://vuejs.org/refacceptsaninternalvalue并返回一个响应一个可修改的ref对象,它只有一个指向其内部值的property.value。Useconstcount=ref(0)console.log(count.value)//0count.value++console.log(count.value)//1源码分析导出函数ref(value?:unknown){returncreateRef(value,false)}ref返回createRef函数的返回值。createRef接收两个参数:rawValue,要转换的值,shallowresponse。functioncreateRef(rawValue:unknown,shallow:boolean){if(isRef(rawValue)){returnrawValue}returnnewRefImpl(rawValue,shallow)}如果rawValue是ref类型,将立即返回rawValue,否则将返回一个RefImpl实例被退回。RefImplclassRefImpl{private_value:Tprivate_rawValue:T//当前ref的依赖publicdep?:Dep=undefinedpublicreadonly__v_isRef=trueconstructor(value:T,publicreadonly__v_isShallow:boolean){this._rawValue=__v_isShallow?价值:toRaw(价值)this._value=__v_isShallow?value:toReactive(value)}getvalue(){trackRefValue(this)returnthis._value}setvalue(newVal){newVal=this.__v_isShallow?newVal:toRaw(newVal)if(hasChanged(newVal,this._rawValue)){this._rawValue=newValthis._value=this.__v_isShallow?newVal:toReactive(newVal)triggerRefValue(this,newVal)}}}RefImpl的构造函数接收两个值:value和__v_isShallow是否是浅响应的。constructor(value:T,publicreadonly__v_isShallow:boolean){//获取原值,如果是浅响应类型,则原值为value;如果不是浅响应类型,则原始值是valuethis._rawValue=__v_isShallow的原始值?value:toRaw(value)//响应数据,如果是浅响应,就是value;否则它会被转换为反应式(只有Object类型会被转换为反应式)this._value=__v_isShallow?value:toReactive(value)}当获取到newRefImpl()的value属性时,会调用trackRefValue收集依赖,返回this._value。导出函数trackRefValue(ref:RefBase){if(shouldTrack&&activeEffect){ref=toRaw(ref)if(__DEV__){trackEffects(ref.dep||(ref.dep=createDep()),{目标:ref,type:TrackOpTypes.GET,key:'value'})}else{//将依赖项收集到ref.dep中trackEffects(ref.dep||(ref.dep=createDep()))}}}与反应式不同,ref的依赖会保存在ref.dep中。当newRefImpl()的value属性被修改时,会调用triggerRefValue来触发依赖。设置值(newVal){newVal=this.__v_isShallow?newVal:toRaw(newVal)//当newVal与旧的原始值不同时,触发依赖if(hasChanged(newVal,this._rawValue)){//更新原始值和响应公式数据this._rawValue=newValthis._value=这个.__v_isShallow?newVal:toReactive(newVal)triggerRefValue(this,newVal)}}shallowRefShallowRef也是通过createRef函数实现的,但是参数shallow为true。exportfunctionshallowRef(value?:unknown){returncreateRef(value,true)}conststate=shallowRef({count:1})effect(()=>{console.log(state.value.count)})//Doesnottriggersideeffectsstate.value.count=2//可以触发副作用state.value={count:3}为什么state.value.count=2不触发副作用?state初始化时,state._value为{count:1},一个普通对象。当使用state.value.count=2设置值时,会先触发get函数返回state._value,然后修改state._value,因为state._value是普通对象,不会触发副作用。但是当使用state.value={count:3}修改时,set函数会被命中,因为新值的内存地址与旧值的内存地址不同,所以会触发副作用。triggerRef强制触发ref的副作用函数。exportfunctiontriggerRef(ref:Ref){triggerRefValue(ref,__DEV__?ref.value:void0)}实现原理很简单,就是主动调用triggerRefValue函数。由于deepresponsiveref会自动触发依赖,triggerRef主要用于在shallowRef内部值发生深度变化后,主动调用triggerRef触发依赖。比如前面的例子:conststate=shallowRef({count:1})effect(()=>{console.log(state.value.count)})//不会触发副作用state.value.count=2//主动触发副作用triggerRef(state)//可以自动触发副作用state.value={count:3}customRef创建自定义ref,显式声明其依赖跟踪和更新触发的控制方法。例如,创建一个debouncedref,即仅在最后一次set调用后的固定间隔后调用它:import{customRef}from'vue'exportfunctionuseDebouncedRef(value,delay=200){lettimeoutreturncustomRef((track,trigger)=>{return{get(){track()返回值},set(newValue){clearTimeout(timeout)timeout=setTimeout(()=>{value=newValuetrigger()},delay)}}})}查看customRef的实现:exportfunctioncustomRef(factory:CustomRefFactory):Ref{returnnewCustomRefImpl(factory)asany}customRef返回一个CustomRefImpl实例。classCustomRefImpl{publicdep?:Dep=undefinedprivatereadonly_get:ReturnType>['get']privatereadonly_set:ReturnType>['set']publicreadonly__v_isRef=trueconstructor(factory:CustomRefFactory){const{get,set}=factory(()=>trackRefValue(this),()=>triggerRefValue(this))this._get=getthis._set=set}获取值(){returnthis._get()}setvalue(newVal){this._set(newVal)}}CustomRefImpl的实现和RefImpl类似,都有value的get和set函数,但是get和set是内部的将调用用户定义的get和set函数。在初始化的时候,会把收集依赖的函数和触发依赖的函数作为参数传递给工厂,方便用户控制依赖收集和触发的时机。综上所述,ref是通过class实现的,通过class的值函数和存储函数来收集和触发依赖。对于深度响应的refs,在给value属性赋值的过程中,会将新的值转化为reactive,从而达到深度响应的效果。