作者:陈大羽头github:KRISACHAN作为一个新特性CompositionAPI,它在Vue3正式发布之前的某个时间发布。根据文档,CompositionAPI是一组低侵入性的函数式API,可以让我们更灵活地“组合”组件的逻辑。不仅在Vue中,在其他框架或者原生JS中也能很好的使用。让我们选择几个重要的CompositionAPI并使用一些简单的示例来了解它们如何在其他项目中使用。注意:本文仅列出每个类别下比较重要的API。如果你想查看所有的,可以点击下面的链接查看:https://github.com/vuejs/vue-...reactiveAPIcreateReactiveObjectcreateReactiveObject函数是reactiveAPI的核心。用于创建反应对象。在分享API之前,我们先来看看它的核心实现。限于篇幅,本文仅展示理解后的简化版代码。代码如下:functioncreateReactiveObject(target,//监听targetisReadonly,//是否只读baseHandlers,//target为Object或Array时处理器支持增删改查数据collectionHandlers//target为Map/WeakMap或Set/WeakSet时的处理器支持增删改查数据){if(target[ReactiveFlags.RAW]&&!(isReadonly&&target[ReactiveFlags.IS_REACTIVE]){//当target为已经是Proxy,直接返回//Exception:callreadonly()inProxyreturntarget}//当前对象已经被监听Outdated,直接返回被监听的对象if(existingProxy){returnexistingProxy}//如果是非数据ObjectArrayMap/WeakMapSet/WeakSet只能监听值的,直接return=newProxy(target,targetType===TargetType.COLLECTION?collectionHandlers:baseHandlers)proxyMap.set(target,proxy)returnproxy}reactive获取一个通用对象并返回该通用对象的反应式代理。相当于2.x的Vue.observable()示例如下:import{reactive,isReactive//判断是否为反应对象}from'https://unpkg.com/@vue/reactivity/dist/reactivity.esm-浏览器。js'constobj={nested:{foo:1},array:[{bar:2}]}constvalue=10constobservedObj=reactive(obj)constobservedValue=reactive(value)console.log(isReactive(observedObj))//trueconsole.log(isReactive(observedValue))//trueshallowReactive只为对象的私有(一级)属性创建一个浅层响应代理,并不会深度递归响应“属性的属性”代理,而是保持原样。例子如下:constobj={nested:{foo:1},array:[{bar:2}]}constvalue=10constunobservedObj=shallowReactive(obj)constunobservedValue=shallowReactive(value)console.log(isReactive(observedObj))//falseconsole.log(isReactive(observedValue))//falseeffectAPIcreateReactiveEffectcreateReactiveEffect是效果API的核心,用于创建监听用户定义的反应对象的函数。在分享API之前,先来看看它的核心实现,限于篇幅,本文只展示理解后的简化版代码,代码如下:functioncreateReactiveEffect(fn,//创建的反应对象用户更改执行回调options={lazy,//是否执行用户函数调度器,//收集数据记录onTrack,//跟踪用户数据的回调onTrigger,//跟踪变化记录onStop,//停止执行allowRecurse//是否允许递归}){consteffect=functionreactiveEffect(){if(!effectStack.includes(effect)){cleanup(effect)//清除effecttry{enableTracking()//将当前effect添加到跟踪中用户数据栈effectStack.push(effect)//添加效果到效果栈activeEffect=effect//将激活的效果变为当前效果returnfn()//执行回调}finally{//删除当前记录effectStack.pop()resetTracking()activeEffect=effectStack[effectStack.length-1]}}}effect.id=uid++effect._isEffect=trueeffect.active=trueeffect.raw=fneffect.deps=[]effect.options=optionsreturneffect}effect效果函数是effectAPI的核心,数据类型为WeakMap,是一个订阅者,用于存储用户自定义函数。这是一个例子:letdummyconstcounter=reactive({num:0})effect(()=>(dummy=counter.num))console.log(dummy===0)//truecounter.num=7console.log(dummy===7)//truerefAPIRefImplRefImpl是refAPI的核心,用于创建ref对象在分享API之前,我们先来看看它的核心实现,代码如下:classRefImpl{private_value//存储当前ref对象的值public__v_isRef=true//判断是否为ref对象的变量(只读)constructor(private_rawValue,//用户传入的原始值publicreadonly_shallow=false//当前ref对象是否为shallowRef){//convert:如果传递给输入的原始值是一个对象,它将被转换函数转换为一个反应对象this._value=_shallow?_rawValue:convert(_rawValue)}getvalue(){//用于跟踪用户输入的值变化//track:effectapitrack函数用于跟踪用户行为,目前是跟踪用户的get操作//toRaw:effectapi的toRaw函数将this转换为用户输入值,this._rawValue)){//当当前ref对象改变时//_rawValue/_value改变//trigger:effectapi的触发函数,由用户输入值和操作类型用于操作。目前,用户输入的值被添加到值映射中。this._rawValue=newValthis._value=this._shallow?newVal:convert(newVal)trigger(toRaw(this),TriggerOpTypes.SET,'value',newVal)}}}ref接受一个参数值并返回一个反应性和可变的ref对象。ref对象有一个指向内部值的属性.value。如果传入的ref是一个对象,就会调用reactive方法进行深度响应转换。例子如下:constcount=ref({name:'鱼头',type:'帅哥'})console.log(count.value.type)//帅哥count.value.type='超帅guy'console.log(count.value.type)//超帅的shallowRef创建一个ref,会跟踪其.value变化操作,但不会对变化的.value进行响应式代理转换(即变化会不调用反应)示例:const__shallowRef=shallowRef({a:1})letdummyeffect(()=>{dummy=__shallowRef.value.a})console.log(dummy)//1__shallowRef.value.a=2console.log(dummy)//1console.log(isReactive(__shallowRef.value))//falsecustomRefcustomRef用于自定义一个ref,可以显式控制依赖跟踪和触发响应,接受一个工厂函数,两个参数track用于跟踪触发器用于触发响应,并返回具有get和set属性的对象。这是一个例子:letvalue=1let_triggerconstcustom=customRef((track,trigger)=>({get(){track()returnvalue},set(newValue){value=newValue_trigger=trigger}}))letdummyeffect(()=>{dummy=custom.value})console.log(dummy)//1custom.value=2console.log(dummy)//1_trigger()console.log(dummy)//2triggerReftriggerRef用于主动触发shallowRef的例子如下:const__shallowRef=shallowRef({a:1})letdummyeffect(()=>{dummy=__shallowRef.value.a})console.log(dummy)//1__shallowRef.value.a=2console.log(dummy)//1console.log(isReactive(__shallowRef.value))//falsetriggerRef(__shallowRef)console.log(dummy)//2computedAPIComputedRefImplComputedRefImpl是refAPI的核心,用于创建计算对象分享前API,我们来看看核心实现,限于篇幅,本文只展示理解后的简化版代码,代码如下:classComputedRefImpl{private_value//当前值private_dirty=true//当前值是否改变publiceffect//effectobjectpublic__v_isRef=true;//指定为ref对象public[ReactiveFlags.IS_READONLY]:boolean//只读构造函数(getter,//getterprivate_setter,//setterisReadonly//只读){this.effect=effect(getter,{lazy:true,scheduler:()=>{if(!this.值映射this._dirty=truetrigger(toRaw(this),TriggerOpTypes.SET,'value')}}})this[ReactiveFlags.IS_READONLY]=isReadonly}getvalue(){if(this._dirty){//返回currentvalue//改变change状态为falsethis._value=this.effect()this._dirty=false}//track:effectapi的track函数用于跟踪用户行为,目前是跟踪用户的getoperationtrack(toRaw(this),TrackOpTypes.GET,'value')returnthis._value}setvalue(newValue){this._setter(newValue)}}computed传入一个getter函数,返回一个ref对象,不能被手动默认修改或传入一个具有get和set函数的对象,创建一个可以手动修改的计算状态。示例如下:constcount1=ref(1)constplus1=computed(()=>count1.value+1)console.log(plus1.value)//2plus1.value++//写操作失败:计算值为只读constcount2=ref(1)constplus2=computed({get:()=>count2.value+1,set:val=>{count2.value=val-1}})console.log(plus2.value)//2plus2。value=0console.log(plus2.value)//0
