当前位置: 首页 > Web前端 > JavaScript

Vue3响应式源码分析

时间:2023-03-26 21:07:52 JavaScript

总结本文摘录了Vue3响应式源码的主要部分,了解其响应式对象的实现。本文源码基于这个入口文件:githubreactive.tsreactive()源码位置:githubreactive.tsexportfunctionreactive(target){returncreateReactiveObject(target,false,mutableHandlers,mutableCollectionHandlers)}reactive内部调用createReactiveObject函数并传入mutableHandlers作为Proxy的handlercreateReactiveObject()源码位置:githubreactive.tsfunctioncreateReactiveObject(target,isReadonly,baseHandlers,collectionHandlers){constproxy=newProxy(target,targetType===TargetType.COLLECTION?collectionHandlers:baseHandlers)returnproxy}可以看出reactive的本质,就是将一个普通的对象转化为一个Proxy。Proxy只支持非空对象,所以reactive函数只能用于对象。consthandler={}newProxy({},handler)//正常运行newProxy(1,handler)//不能创建以非对象为目标或处理程序的代理如果用于原始类型的响应,Vue3应该使用提供的ref函数。MutableHandlers源码位置:githubbaseHandlers.tsmutableHandlers和mutableCollectionHandlers作为newProxy的第二个参数,是reactive函数的核心逻辑,重点关注其get和set属性。exportconstmutableHandlers={get:createGetter(),set:createSetter(),deleteProperty,has,ownKeys,}createGetter()源码位置:githubbaseHandlers.tsfunctioncreateGetter(isReadonly=false,shallow=false){returnfunctionget(target:target,key:string|symbol,receiver:object){//里面的代码比较长,所以分成几个部分来理解//第1部分——处理内部变量//第2部分——处理数组//part3-处理其他情况}}part1-处理内部变量//tssyntaxconstenumReactiveFlags{SKIP='__v_skip',IS_REACTIVE='__v_isReactive',IS_READONLY='__v_isReadonly',IS_SHALLOW='__v_isShallow',RAW='__v_raw'}if(key===ReactiveFlags.IS_REACTIVE){return!isReadonly}elseif(key===ReactiveFlags.IS_READONLY){returnisReadonly}elseif(key===ReactiveFlags.IS_SHALLOW){returnshallow}elseif(key===ReactiveFlags.RAW&&receiver===(isReadonly?shallow?shallowReadonlyMap:readonlyMap:浅的?shallowReactiveMap:reactiveMap).get(target)){返回target}使用reactive()生成的对象包含ReactiveFlags对应的属性,可以输出如下效果:RunJSDemo使用shallowReactive()生成对象,只有根节点的属性是响应式的,与Vue2中的response公式变量的属性是一样的。看这个例子:Vue3shallowReactivepart2-HandlingArraysconsttargetIsArray=isArray(target)if(!isReadonly&&targetIsArray&&hasOwn(arrayInstrumentations,key)){returnReflect.get(arrayInstrumentations,key,receiver)}代理是一个数组,并且调用使用数组原型方法时,其实是先获取方法名,然后使用这个方法设置对应的值,比如下面的代码:constarr=[{count:1},{count:2}]constproxyArray=newProxy(arr,{get(target,prop,receiver){console.log(`get${prop}`)返回Reflect.get(target,prop,receiver)},set(target,prop,receiver){console.log(`set${prop}`)returnReflect.set(target,prop,receiver)}})proxyArray.push({count:3})//依次输出getpushgetlengthset2setlength看实际运行效果:JavascriptProxyanarraypart3-处理其他情况eturnres}if(!isReadonly){track(target,TrackOpTypes.GET,key)}if(shallow){returnres}if(isRef(res)){//refunwrapping-跳过unwrapfor数组+整数键。返回targetIsArray&&isIntegerKey(key)?res:res.value}if(isObject(res)){//也将返回值转换为代理。我们在此处执行isObject检查//以避免无效值警告。还需要在这里延迟访问readonly//和reactive以避免循环依赖。返回是只读的?readonly(res):reactive(res)}returnres在用reactive初始化的变量中,如果包含ref类型的值,则取值不需要带.value例如:constcount=ref(0)conststate=reactive({count:count})//下面两行可以得到count值console.log(count.value)console.log(state.count)实际运行效果见:Vue3reactiveobjectwithrefcreateSetter()源码位置:githubbaseHandlers.tsfunctioncreateSetter(shallow=false){returnfunctionset(target:object,key:string|symbol,value:unknown,receiver:object):boolean{//第1部分-赋值//第2部分-触发事件}}第1部分-赋值letoldValue=(targetasany)[key]if(isReadonly(oldValue)&&isRef(oldValue)&&!isRef(value)){returnfalse}if(!shallow){if(!isShallow(value)&&!isReadonly(value)){oldValue=toRaw(oldValue)value=toRaw(value)}if(!isArray(target)&&isRef(oldValue)&&!isRef(value)){oldValue.value=valuereturntrue}}else{//在浅层模式下,无论是否响应,对象都按原样设置}consthadKey=isArray(target)&&isIntegerKey(key)?数字(键)