首页>>前端>>Vue->vue响应式原理源码剖析

vue响应式原理源码剖析

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

vue2更新到vue3对数据的响应式,做了重大的调整。

先回顾下vue2的响应式怎么做的

vue2对象响应式原理:Object.defineProperty()

vue2数组响应式原理:覆盖可以修改数组7个方法,从数组原型中获取这7个方法,并覆盖为可以发送更新通知的函数实现

对象的属性劫持

//具体定义指定的key拦截器functiondefineReactive(obj,key,val){//递归遍历observe(val)//val实际上是一个闭包Object.defineProperty(obj,key,{get(){returnval},set(newVal){if(newVal!==val){//val可能是对象observe(newVal)notifyUpdate()val=newVal}}})}

数组属性的劫持

//修改数组的7个api的原型constoriginalProto=Array.prototypeconstarrayProto=Object.create(originalProto);['push','pop','shift','unshift','splice','reverse','sort'].forEach(method=>{arrayProto[method]=function(){//做之前的事情originalProto[method].apply(this,arguments)//通知更新notifyUpdate()}})

数据响应

//思想:递归遍历传入obj,定义每个属性的拦截functionobserve(obj){if(typeofobj!=='object'||obj==null){returnobj}//判断类型:如果是数组则替换它的原型if(Array.isArray(obj)){Object.setPrototypeOf(obj,arrayProto)}else{constkeys=Object.keys(obj)for(letindex=0;index<keys.length;index++){constkey=keys[index]//对obj每个key执行拦截defineReactive(obj,key,obj[key])}}}

更新处理

functionnotifyUpdate(){console.log('页面更新!')}constdata={foo:'foo',bar:{a:1},tua:[1,2,3]}observe(data)//1.普通更新//data.foo='foooooooo'//2.嵌套属性更新//data.bar.a=10//data.dong='lalala'//nook//3.赋值是对象//data.bar={a:10}//4.数组//data.tua.push(4)

以上方式会出现什么问题呢?

需要响应化的数据较大,递归遍历性能不好、消耗较大

新增或删除属性无法监听

数组响应化需要额外实现

修改语法有限制

所以在vue3跟新做了更加优化方案

vue3的响应式原理的实现

vue3响应式原理:利用Proxy对象对数据拦截

//WeakMap弱引用的方式缓存代理数据和原始数据consttoProxy=newWeakMap()//形如obj:observedconsttoRaw=newWeakMap()//形如observed:objfunctionisObject(obj){returntypeofobj==='object'||obj===null}functionhasOwn(obj,key){returnobj.hasOwnProperty(key)}//响应对象数据functionreactive(obj){if(!isObject(obj)){returnobj}//查找缓存if(toProxy.has(obj)){returntoProxy.get(obj)}//传入obj就是代理对象,此时不用反复代理if(toRaw.has(obj)){returnobj}constobserved=newProxy(obj,{get(target,key,receiver){//访问constres=Reflect.get(target,key,receiver)console.log(`获取${key}:${res}`)//依赖收集track(target,key)returnisObject(res)?reactive(res):res},set(target,key,value,receiver){//新增和更新consthadKey=hasOwn(target,key)//ADD或SETconstoldVal=tarGET@[key]constres=Reflect.set(target,key,value,receiver)if(!hadKey){console.log(`新增${key}:${value}`)trigger(target,'ADD',key)}elseif(oldVal!==value){console.log(`设置${key}:${value}`)trigger(target,'SET',key)}returnres},deleteProperty(target,key){//删除consthadKey=hasOwn(target,key)constres=Reflect.deleteProperty(target,key)//key存在并且删除成功if(res&&hadKey){console.log(`删除${key}:${res}`)trigger(target,'DELETE',key)}returnres}})//缓存toProxy.set(obj,observed)toRaw.set(observed,obj)returnobserved}//每一个属性的响应回调方法constactiveReativeEffectStack=[]//依赖收集执行//基本结构{target:{key:[eff1,eff2]}}lettargetsMap=newWeakMap()functiontrack(target,key){//从栈中获取响应函数consteffect=activeReativeEffectStack[activeReativeEffectStack.length-1]if(effect){letdepsMap=targetsMap.get(target)if(!depsMap){//首次访问targetdepsMap=newMap()targetsMap.set(target,depsMap)}//存放keyletdeps=depsMap.get(key)if(!deps){deps=newSet()depsMap.set(key,deps)}if(!deps.has(effect)){deps.add(effect)}}}//数据变化响应回调functioneffect(fn){//1.异常处理//2.执行函数//3.放置到activeReativeEffectStackconstrxEffect=function(...args){try{activeReativeEffectStack.push(rxEffect)returnfn(...args)//执行函数触发依赖收集}finally{activeReativeEffectStack.pop()}}rxEffect()//默认立即执行returnrxEffect}//触发target.key对应响应函数functiontrigger(target,type,key){//获取依赖表constdepsMap=targetsMap.get(target)if(depsMap){//获取响应函数集合constdeps=depsMap.get(key)consteffects=newSet()if(deps){//执行所有响应函数deps.forEach(effect=>{//effect()effects.add(effect)})}//数组新增或删除if(type==='ADD'||type==='DELETE'){if(Array.isArray(target)){constdeps=depsMap.get('length')if(deps){deps.forEach(effect=>{effects.add(effect)})}}}//获取已存在的DepSet执行effects.forEach(effect=>effect())}}constdata={foo:'foo',bar:{a:1}}constreact=reactive(data)//1.获取//react.foo//ok//2.设置已存在属性//react.foo='foooooooo'//3.设置不存在属性//react.baz='bazzzzzz'//4.嵌套对象//react.bar.a=10//避免重复代理//console.log(reactive(data)===react)//true//reactive(react)effect(()=>{console.log('count发生了变化:',react.foo)//dom})react.foo='fooooooo'

vue响应式数据更新的流程


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