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

最全最详尽的vue3响应式数据分析,vue3源码分析持续更新中

时间:2023-03-31 21:18:37 vue.js

这是我新坑的第一篇文章,这个坑就是vue3,接下来我将围绕vue3进行一系列的操作,包括但不限于:完整源码分析jsx项目最佳实践个性化jsx解决方案vue3生态库开发(目前有一个在进行中)和可能定制一个vue3运行时关于源码分析,网站已上线,vue3源码analysis,最佳实践,网站是逐行代码形式的分析,更侧重于源码,而掘金上分享的文章类似于总结,会写成更复杂的文章结构.如果你想继续关注vue3的源码,可以打开之前的网址关注我。所以,我们走吧!vue3最大的变化就是对响应式原理的重构,以及新发布的compositionapi。本文主要针对前者,深入剖析响应式在vue3中是如何实现的。我们以reactiveAPI为例,constComp={setup(){conststate=reactive({a:'jokcy'})return()=>{returnstate.a=e.target.value}/>}}}我们看上面的例子,这个例子很简单,创建一个组件,他有一个响应式数据对象,然后绑定input中的值render什么是state.a,他的onChange会修改state.a。这是一个非常简单直观的数据绑定的例子,而这个逻辑的根本原因是当我们调用state.a=xxx时,vue会重新渲染我们返回的render函数来更新节点。这篇文章就是来看看我们通过reactive创建的对象的魔力,可以帮助我们完成这个任务。实际上,API本身非常简单。传入一个对象并返回一个反应对象。创建方法是createReactiveObjectexportfunctionreactive(target:object){//如果试图观察只读代理,返回只读版本。if(target&&(targetasTarget)[ReactiveFlags.IS_READONLY]){返回目标;}returncreateReactiveObject(target,false,mutableHandlers,mutableCollectionHandlers);}functioncreateReactiveObject(target:Target,isReadonly:boolean,baseHandlersection:ProxyHandler,){//前面是判断逻辑对象已经被代理等,这里就不展示了。constobserved=newProxy(target,collectionTypes.has(target.constructor)?collectionHandlers:baseHandlers);def(target,isReadonly?ReactiveFlags.READONLY:ReactiveFlags.REACTIVE,observed);returnobserved;}那么这里最重要的就是新建Proxy了。可以说理解vue3的responsive原理过程就是理解创建这个proxy的过程,而理解这个过程,主要是看第二个参数,也就是这里的collectionHandlers或者baseHandlers,大部分都是后者,前者主要针对Set、Map等。然后我们看看baseHandlers:exportconstmutableHandlers:ProxyHandler={get,set,deleteProperty,has,ownKeys,};可以看出vue代理了这些操作,那么我们一一看看这些操作做了什么:functionget(target:object,key:string|symbol,receiver:object){//...足够的内部键//一些关于数组的特殊处理consttargetIsArray=isArray(target);如果(targetIsArray&&hasOwn(arrayInstrumentations,key)){returnReflect.get(arrayInstrumentations,key,receiver);}//获取请求值constres=Reflect.get(target,key,receiver);//...如果是内部取值则直接返回resif(!isReadonly){track(target,TrackOpTypes.GET,key);}//一些返回的处理returnres;}这里的重点是track(target,TrackOpTypes.GET,key);,这个就是我们正常取值的时候要执行的代码:exportfunctiontrack(target:object,type:TrackOpTypes,key:unknown){if(!shouldTrack||activeEffect===undefined){返回;}让depsMap=targetMap.get(target);如果(!depsMap){targetMap.set(目标,(depsMap=newMap()));}letdep=depsMap.get(key);如果(!部门){depsMap.set(key,(dep=newSet()));}if(!dep.has(activeEffect)){dep.add(activeEffect);activeEffect.deps.push(dep);如果(__DEV__&&activeEffect.options.onTrack){activeEffect.options.onTrack({效果:activeEffect,目标,类型,键,});}}}好了,重点来了,我们逐行分析第一个if,它是根据环境变量shouldTrack来判断是否跟踪的,如果你看过我源码分析中processComponent那一章,那么你现在应该觉得豁然开朗了。因为在processComponent中执行setup的时候,我们特地把track关掉了,那个时候,我们把shouldTrack改成了false。letdepsMap=targetMap.get(target),targetMap是一个map,用来记录所有的响应对象。然后,如果该对象当前未被记录,则重新记录。这个target也对应一个map,会为每个key创建一个set。最后,我们需要记录一下这次的效果。这里所谓的效果是什么?是当前调用此对象的函数。在我们的示例中,返回的是渲染函数。这个函数执行的时候会调用state.a,所以会进入代理获取。这个时候proxy调用track,此时的activeEffect就是render方法。.这里的效果是当state.a发生变化时,我们需要重新执行render方法进行渲染。那么他是什么时候成立的呢?我们在mount章节提到,在执行render方法的时候,执行了这么一段代码instance.update=effect(functioncomponentEffect()...),就是在这里调用的effect方法中将activeEffect记录为componentEffect,而render方法在此componentEffect中运行。另外,这个getHandler里面有一句代码也是蛮有意思的:if(isObject(res)){returnisReadonly?readonly(res):reactive(res);}Reactive会在上面执行,属于延迟处理,readonly不会执行reactive。OK,到这里我们已经知道了,在执行render函数的时候,因为我们调用了state.a,这个函数就相当于依赖了state.a,在vue3中叫做effect。那么在下一篇文章中,我们会讲到当state.a发生变化时,这些效果是如何被调用的,敬请期待。