当前位置: 首页 > 科技观察

Vue2剥茧-响应式系统数组

时间:2023-03-16 12:56:36 科技观察

Sceneimport{observe}from"./reactive";importWatcherfrom"./watcher";constdata={list:["hello"],};observe(data);constupdateComponent=()=>{for(constitemofdata.list){console.log(item);}};newWatcher(updateComponent);data.list=["hello","liang"];首先,您可以考虑一下将输出什么。......虽然list的值是一个数组,但是我们将data.list作为一个整体赋值,所以data.list的集合还是会被触发,会触发Watcher重新执行。输出如下:Scenario2import{observe}from"./reactive";importWatcherfrom"./watcher";constdata={list:["hello"],};observe(data);constupdateComponent=()=>{for(constitemofdata.list){console.log(item);}};newWatcher(updateComponent);data.list.push("liang");你可以考虑一分钟会输出什么。...这次调用了push方法,但是我们没有对push方法做任何事情,所以不会触发Watcher。解决方案为了让数组的push等方法也生效,我们需要重写。通过代理模式,我们可以先保存数组原有的方法,然后执行,添加自己的额外操作。/**不对该文件进行类型检查,因为流不能很好地处理*数组原型上的动态访问方法*//*exportfunctiondef(obj,key,val,enumerable){Object.defineProperty(obj,key,{value:val,enumerable:!!enumerable,writable:true,configurable:true,});}*/import{def}from"./util";constarrayProto=Array.prototype;exportconstarrayMethods=Object.create(arrayProto);constmethodsToPatch=["push","pop","shift","unshift","splice","sort","reverse",];/***拦截变异方法并发出事件*/methodsToPatch。forEach(function(method){//缓存原始方法constoriginal=arrayProto[method];def(arrayMethods,method,functionmutator(...args){constresult=original.apply(this,args);/*****************这里相当使用了对象集需要通知watcher************************///待补充/********************************************************************************/返回结果;});});当调用数组的push或其他方法时,相当于我们之前重写属性的集合。上面需要添加的是在dep中通知Watcher导出函数defineReactive(obj,key,val,shallow){constproperty=Object.getOwnPropertyDescriptor(obj,key);//读取可能由用户定义的get和setconstgetter=property&&property.get;constsetter=property&&property.set;//如果没有传入val则手动赋值if((!getter||setter)&&arguments.length===2){val=obj[key];}constdep=newDep();//持有一个Dep对象来保存所有依赖于变量的WatcherletchildOb=!shallow&&observe(val);Object.defineProperty(obj,key,{enumerable:true,configurable:true,get:functionreactiveGetter(){constvalue=getter?getter.call(obj):val;if(Dep.target){dep.depend();}返回值;},设置:functionreactiveSetter(newVal){constvalue=getter?getter.call(obj):val;if(setter){setter.call(obj,newVal);}else{val=newVal;}dep.notify();},});}如上面代码,前面的dep是通过闭包,每个属性都有自己的dep,负责收集Watcher,通知Watcher。那么对于数组的话,我们的dep放在哪里比较方便呢?回顾当前结构。const数据={列表:["你好"],};观察(数据);constupdateComponent=()=>{for(constitemofdata.list){console.日志(项目);}};新观察者(更新组件);上面的代码执行后,就会有下图所示的结构。list属性在闭包中有Dep属性,通过newWatcher收集包含updateCompnent函数的Watcher。同时,因为list的值["hello"]是一个数组,也就是一个对象,我们从上一篇深入响应式系统的文章中得知,它也会调用Observer函数。那么,我是否也应该为Observer添加一个Dep?这样,当我们调用数组方法修改['hello']的值时,只需要通知Observer中的Dep即可。收集依赖代码实现按照上面的思路完善Observer类。exportclassObserver{constructor(value){/******Add***************************/this.dep=newDep();/************************************/这。步行(价值);}/***遍历对象的所有属性,调用defineReactive*拦截对象属性的get和set方法*/walk(obj){constkeys=Object.keys(obj);for(leti=0;i{for(constitemofdata.list){console.log(item);}};newWatcher(updateComponent);data.list.push("liang");这样,当调用push方法时,会触发对应的Watcher执行updateComponent函数,当前依赖变为如下:arraysResponsive我们解决了三个问题,在哪里放依赖,收集依赖和通知依赖,我们拿它和普通的对象属性做个对比。