首页>>前端>>Vue->手摸手教你简单快速实现vue3 reactivity ref computed

手摸手教你简单快速实现vue3 reactivity ref computed

时间:2023-11-29 本站 点击:1

先谈谈vue3的响应式实现

vue3通过es6的proxy来实现数据的get和set的劫持。get时将订阅函数加入依赖,set时执行依赖列表。

与Object.defineproperty不同的是,proxy监听的是整个对象,因此可以监听到新增的对象属性。

reactivity的实现

看下面的代码 我们如何实现reactive呢和effect呢?

letproduct=reactive({price:5,quantity:2})lettotal=0effect(()=>{total=product.price*product.quantity})console.log('beforeupdatedquantitytotal='+total)product.quantity=3console.log('afterupdatedquantitytotal='+total)

reactive的功能是数据的get和set的接触,需要使用es6的proxy实现。 effect用来进行依赖收集。

consttargetMap=newWeakMap()//targetMapstorestheeffectsthateachobjectshouldre-runwhenit'supdatedletactiveEffect=null//Theactiveeffectrunningfunctiontrack(target,key){if(activeEffect){//<------ChecktoseeifwehaveanactiveEffect//Weneedtomakesurethiseffectisbeingtracked.letdepsMap=targetMap.get(target)//GetthecurrentdepsMapforthistargetif(!depsMap){//Thereisnomap.targetMap.set(target,(depsMap=newMap()))//Createone}letdep=depsMap.get(key)//Getthecurrentdependencies(effects)thatneedtoberunwhenthisissetif(!dep){//Thereisnodependencies(effects)depsMap.set(key,(dep=newSet()))//CreateanewSet}dep.add(activeEffect)//Addeffecttodependencymap}}functiontrigger(target,key){constdepsMap=targetMap.get(target)//Doesthisobjecthaveanypropertiesthathavedependencies(effects)if(!depsMap){return}letdep=depsMap.get(key)//Iftherearedependencies(effects)associatedwiththisif(dep){dep.forEach((effect)=>{//runthemalleffect()})}}functionreactive(target){consthandler={get(target,key,receiver){letresult=Reflect.get(target,key,receiver)track(target,key)//Ifthisreactiveproperty(target)isGETinsidethentracktheeffecttorerunonSETreturnresult},set(target,key,value,receiver){letoldValue=tarGET@[key]letresult=Reflect.set(target,key,value,receiver)if(result&&oldValue!=value){trigger(target,key)//Ifthisreactiveproperty(target)haseffectstorerunonSET,triggerthem.}returnresult},}returnnewProxy(target,handler)}//对effect函数里的变量都来收集依赖functioneffect(eff){activeEffect=effactiveEffect()activeEffect=null}letproduct=reactive({price:5,quantity:2})lettotal=0effect(()=>{total=product.price*product.quantity})console.log('beforeupdatedquantitytotal='+total)product.quantity=3console.log('afterupdatedquantitytotal='+total)console.log('Updatedquantityto='+product.quantity)

在effect中执行eff, eff中会进行数据的get操作,从而在reactive中的get进行依赖收集。

当更改数据时,执行reactive中的set更新数据。

ref的实现

把上面例子中的total尝试改为用ref包裹的变量。

lettotal=0

那么ref怎么实现呢? 方法一:

functionref(intialValue){returnreactive({value:initialValue})

这种方法使用reactive来实现,也可以直接实现。

functionref(raw){constr={getvalue(){track(r,'value')returnraw},setvalue(newVal){raw=newValtrigger(r,'value')},}returnr}

完整demo如下。

consttargetMap=newWeakMap()//targetMapstorestheeffectsthateachobjectshouldre-runwhenit'supdatedletactiveEffect=null//Theactiveeffectrunningfunctiontrack(target,key){if(activeEffect){//<------ChecktoseeifwehaveanactiveEffect//Weneedtomakesurethiseffectisbeingtracked.letdepsMap=targetMap.get(target)//GetthecurrentdepsMapforthistargetif(!depsMap){//Thereisnomap.targetMap.set(target,(depsMap=newMap()))//Createone}letdep=depsMap.get(key)//Getthecurrentdependencies(effects)thatneedtoberunwhenthisissetif(!dep){//Thereisnodependencies(effects)depsMap.set(key,(dep=newSet()))//CreateanewSet}dep.add(activeEffect)//Addeffecttodependencymap}}functiontrigger(target,key){constdepsMap=targetMap.get(target)//Doesthisobjecthaveanypropertiesthathavedependencies(effects)if(!depsMap){return}letdep=depsMap.get(key)//Iftherearedependencies(effects)associatedwiththisif(dep){dep.forEach((effect)=>{//runthemalleffect()})}}functionreactive(target){consthandler={get(target,key,receiver){letresult=Reflect.get(target,key,receiver)track(target,key)//Ifthisreactiveproperty(target)isGETinsidethentracktheeffecttorerunonSETreturnresult},set(target,key,value,receiver){letoldValue=tarGET@[key]letresult=Reflect.set(target,key,value,receiver)if(result&&oldValue!=value){trigger(target,key)//Ifthisreactiveproperty(target)haseffectstorerunonSET,triggerthem.}returnresult},}returnnewProxy(target,handler)}//对effect函数里的变量都来收集依赖functioneffect(eff){activeEffect=effactiveEffect()activeEffect=null}functionref(raw){constr={getvalue(){track(r,'value')returnraw},setvalue(newVal){raw=newValtrigger(r,'value')},}returnr}letproduct=reactive({price:5,quantity:2})lettotal=ref()effect(()=>{total.value=product.price*product.quantity})console.log('beforeupdatedquantitytotal='+total.value)product.quantity=3console.log('afterupdatedquantitytotal='+total.value)console.log('Updatedquantityto='+product.quantity)

computed的实现

computed的实现,依赖于参数的执行结果。

computed的用法:

lettotal=computed(()=>{returnproduct.price*product.quantity})

computed的实现依赖于ref:

functioncomputed(getter){letresult=ref()effect(()=>(result.value=getter()))returnresult}

完整demo:

consttargetMap=newWeakMap()//targetMapstorestheeffectsthateachobjectshouldre-runwhenit'supdatedletactiveEffect=null//Theactiveeffectrunningfunctiontrack(target,key){if(activeEffect){//<------ChecktoseeifwehaveanactiveEffect//Weneedtomakesurethiseffectisbeingtracked.letdepsMap=targetMap.get(target)//GetthecurrentdepsMapforthistargetif(!depsMap){//Thereisnomap.targetMap.set(target,(depsMap=newMap()))//Createone}letdep=depsMap.get(key)//Getthecurrentdependencies(effects)thatneedtoberunwhenthisissetif(!dep){//Thereisnodependencies(effects)depsMap.set(key,(dep=newSet()))//CreateanewSet}dep.add(activeEffect)//Addeffecttodependencymap}}functiontrigger(target,key){constdepsMap=targetMap.get(target)//Doesthisobjecthaveanypropertiesthathavedependencies(effects)if(!depsMap){return}letdep=depsMap.get(key)//Iftherearedependencies(effects)associatedwiththisif(dep){dep.forEach((effect)=>{//runthemalleffect()})}}functionreactive(target){consthandler={get(target,key,receiver){letresult=Reflect.get(target,key,receiver)track(target,key)//Ifthisreactiveproperty(target)isGETinsidethentracktheeffecttorerunonSETreturnresult},set(target,key,value,receiver){letoldValue=tarGET@[key]letresult=Reflect.set(target,key,value,receiver)if(result&&oldValue!=value){trigger(target,key)//Ifthisreactiveproperty(target)haseffectstorerunonSET,triggerthem.}returnresult},}returnnewProxy(target,handler)}//对effect函数里的变量都来收集依赖functioneffect(eff){activeEffect=effactiveEffect()activeEffect=null}functionref(raw){constr={getvalue(){track(r,'value')returnraw},setvalue(newVal){raw=newValtrigger(r,'value')},}returnr}functioncomputed(getter){letresult=ref()effect(()=>(result.value=getter()))returnresult}letproduct=reactive({price:5,quantity:2})lettotal=computed(()=>{returnproduct.price*product.quantity})console.log('beforeupdatedquantitytotal='+total.value)product.quantity=3console.log('afterupdatedquantitytotal='+total.value)console.log('Updatedquantityto='+product.quantity)

参考资料

https://github.com/Code-Pop/vue-3-reactivity

https://www.bilibili.com/video/BV1SZ4y1x7a9?from=search&seid=16256770774635613102&spm_id_from=333.337.0.0

https://v3.cn.vuejs.org/api/basic-reactivity.html#reactive

https://developer.mozilla.org/zh-CN/docs/Mozilla/Add-ons/WebExtensions/API/proxy


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Vue/822.html