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

Vue3响应式原理+手写reactive

时间:2023-03-31 22:26:30 vue.js

reactive##说起Vue3,Vue3已经到了rc4版本了。4月份发布内测时,前端圈子一片火爆。不知道大家是不是已经开始学习了,还没有整理一些资源。现在开始学习不应该太晚[狗头]vue-next仓库20200723Vue3正式发布beta文档Vue3Roadmap&FAQVue3仓库已合并780多个PRYouda的Vue3类在VueMastery:Vue3DeepDivewithEvanYou202007Youda的专访前端客厅程序谈Vue3202005过程:制作Vue3202004友达-谈Vue.js3.0Beta官方直播2018VueConf杭州友达演讲视频谈Vue3Vue2响应式原理回顾ObjectResponsive:遍历每个key,定义getter通过Object.definePropertyAPI,setter//伪代码函数observe(){if(typeofobj!='object'||obj==null){return}if(Array.isArray(obj)){Object.setPrototypeOf(obj,arrayProto)}else{constkeys=Object.keys()for(leti=0;i{arrayProto[key]=function(){originalProto[key].apply(this.arguments)notifyUpdate()}})Vue2响应式痛点是递归的,增加/删除属性消耗很大,需要额外实现单独的API数组,需要额外实现MapSetClass等数据类型,无法响应修改语法。Vue3响应方案采用ES6的Proxy进行数据响应,解决了以上Vue2的所有痛点。Proxy可以在目标对象上加一层拦截/代理。外部对目标对象的操作都会被这一层拦截。与Object.defineProperty相比,Proxy支持的Object操作非常全面:get,set,has,deleteProperty,ownKeys,defineProperty等//反应式伪代码函数reactice(obj){returnnewProxy(obj,{get(target,key,接收器){constret=Reflect.get(target,key,receiver)returnisObject(ret)?reaction(ret):ret},set(target,key,val,receiver){constret=Reflect.set(target,key,val,receiver)returnret},deleteProperty(target,key){constret=Reflect.deleteProperty(target,key)returnret},})}响应式原理通过effect声明依赖响应式数据的函数cb(如视图渲染函数render函数),执行cb函数.在执行过程中,会触发响应式数据getter,在响应式数据getter中收集track依赖:建立data&cb的映射关系,存储到targetMap中。改变响应式数据时,触发trigger,根据targetMap找到关联的cb执行映射关系targetMap结构:targetMap:WeakMap{target:Map{key:Set[cb1,cb2...]}}structure//mini-vue3.js/*创建响应式数据*/functionreactice(obj){}/*declareresponseFunctioncb(依赖响应式数据)*/functioneffect(cb){}/*依赖集合:建立数据&cb映射关系*/functiontrack(target,key){}/*触发更新:根据映射关系执行cb*/functiontrigger(target,key){}reactive/*创建响应式数据*/functionreactive(obj){//Proxy:http://es6.ruanyifeng.com/#docs/proxy//Proxy相当于object在外层添加拦截//Proxy递归是惰性的,需要加入递归逻辑//反映:http://es6.ruanyifeng.com/#docs/reflect//Reflect:用于执行对象的默认操作,更规范更友好ndly,可以理解为操作对象的集合//Proxy和Object方法Reflect都有相应的if(!isObject(obj))returnobjconstobserved=newProxy(obj,{get(target,key,receiver){constret=Reflect.get(目标et,key,receiver)console.log('getter'+ret)//轨迹采集依赖track(target,key)returnreactive(ret)},set(target,key,val,receiver){constret=Reflect.set(target,key,val,receiver)console.log('setter'+key+':'+val+'=>'+ret)//触发更新trigger(target,key)returnret},deleteProperty(target,key){constret=Reflect.deleteProperty(target,key)console.log('delete'+key+':'+ret)//触发更新trigger(target,key)returnret},})returnobserved}effect/*声明响应函数cb*/consteffectStack=[]functioneffect(cb){//函数的高级封装constrxEffect=function(){//1.捕获异常//2.Fn被弹出thestack//3.Executefntry{effectStack.push(rxEffect)returncb()}finally{effectStack.pop()}}//初始执行一次用于初始依赖收集rxEffect()returnrxEffect}track/*依赖收集:建立数据&cb映射关系*/consttargetMap=newWeakMap()functiontrack(target,key){//存储映射关系consteffectFn=effectStack[effectStack.length-1]//取出栈顶函数if(effectFn){letdepsMap=targetMap.get(target)if(!depsMap){depsMap=newMap()targetMap.set(target,depsMap)}letdeps=depsMap.get(key)if(!deps){deps=newSet()depsMap.set(key,deps)}deps.add(effectFn)}}trigger/*触发更新:根据映射关系*/functiontrigger(target,key){constdepsMap=targetMap.get(target)if(depsMap){constdeps=depsMap.get(key)if(deps){deps.forEach(effect=>effect())}}}测试演示{{msg}}

效果:如果想获取上面的代码,放在这个仓库:mini-vue3-reactive