数组setimport{observe}from"./reactive";从“./watcher”导入观察者;constdata={list:[1,2],};observe(data);constupdateComponent=()=>{console.log(data.list);};newWatcher(updateComponent);list[0]=3;list[0]会触发updateComponent的重新执行?你可以先考虑一下。……答案是否定的,我们只能通过重写的push、splice等方式来触发数组的更新,详见Vue2剥茧-响应式系统数组。如果我们要替换数组的某个元素,可以转一圈,使用splice来实现。从“./reactive”导入{观察};从“./watcher”导入观察者;const数据={列表:[1,2],};观察(数据);constupdateComponent=()=>{console.log(data.list);};newWatcher(updateComponent);//list[0]=3;data.list.splice(0,1,3);每次都这样写太麻烦了,我们可以提供一个set方法给用户使用。/***设置一个对象的属性。添加新属性并*如果该属性不存在则触发更改通知*已经存在。*/exportfunctionset(target,key,val){if(Array.isArray(target)){target.length=Math.max(target.length,key);target.splice(key,1,val);返回值;}//target是一个对象//...}那么我们直接使用set方法就可以了。import{observe,set}from"./reactive";从“./watcher”导入观察者;const数据={列表:[1,2],};观察(数据);constupdateComponent=()=>{console.log(data.list);};newWatcher(updateComponent);//list[0]=3;//data.list.splice(0,1,3);set(data.list,0,3);Arraydel和arrayset一样,我们顺便提供了一个del方法,支持数组响应式删除一个元素。/***删除属性并在必要时触发更改。*/exportfunctiondel(target,key){if(Array.isArray(target)&&isValidArrayIndex(key)){target.splice(key,1);返回;}//目标是一个对象//...}objectsetimport{observe,set,del}from"./reactive";从“./watcher”导入观察者;常量数据={obj:{a:1,b:2,},};观察(数据);constupdateComponent=()=>{constc=data.obj.c?数据.obj.c:0;console.log(data.obj.a+data.obj.b+c);};newWatcher(updateComponent);data.obj.c=3;虽然在updateComponent方法中使用了obj的c属性,但是在调用observe之前data.obj中并没有c属性,所以c属性不是响应式的。当我们修改data.obj.c的值时,并不会触发updateComponent的执行。如果你想变得反应式,一种方法是在开头定义c属性。constdata={obj:{a:1,b:2,c:null,},};观察(数据);constupdateComponent=()=>{constc=data.obj.c?数据.obj.c:0;console.log(data.obj.a+data.obj.b+c);};newWatcher(updateComponent);data.obj.c=3;另一种方法是通过set来设置新的属性现在,在set中我们可以将新添加的属性设置为响应式的。/***设置一个对象的属性。添加新属性并*如果该属性不存在则触发更改通知*已经存在。*/exportfunctionset(target,key,val){if(Array.isArray(target)){target.length=Math.max(target.length,key);target.splice(key,1,val);返回值;}//当target是一个对象时//key已经存在于targetif(keyintarget&&!(keyinObject.prototype)){target[key]=val;返回值;}constob=target.__ob__;//target不是响应数据if(!ob){target[key]=val;返回值;}//将当前键变成响应式defineReactive(target,key,val);returnval;}回到我们之前的程序:import{observe,set,del}from"./reactive";importWatcherfrom"./watcher";常量数据={obj:{a:1,b:2,},};观察(数据);constupdateComponent=()=>{constc=data.obj.c?数据.obj.c:0;console.log(data.obj.a+data.obj.b+c);};constob=new观察者(updateComponent);设置(data.obj,“c”,3);虽然通过set添加了属性,但是此时不会重新触发Watcher。究其原因,我们来看依赖图。虽然属性c有一个Dep对象,但是由于没有调用依赖属性c的Watcher,所以没有收集依赖。当然我们可以在设置后手动调用一次相应的Watcher。常量数据={obj:{a:1,b:2,},};观察(数据);constupdateComponent=()=>{constc=data.obj.c?数据.obj.c:0;控制台.log(data.obj.a+data.obj.b+c);};constob=newWatcher(updateComponent);set(data.obj,"c",3);ob.update();//手动调用Watcherdata.obj.c=4;这样,当执行data.obj.c=4时,就会触发Watcher的执行。那么我们能不能把触发对应Watcher的逻辑放到set函数中呢?可以看到obj里面还有一个Dep,其实就是为一个数组准备的。该部门不收集任何东西。让我们修改代码来收集它://读取get并设置用户可能自己定义的constgetters=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();if(childOb){/******新位置************************/childOb.dep.depend();/**************************************/if(Array.isArray(value)){//childOb.dep.depend();//原始位置dependArray(value);}}}返回值;},设置:函数reactiveSetter(newVal){constvalue=getter?getter.call(obj):val;如果(setter){setter.call(obj,newVal);}else{val=newVal;}childOb=!shallow&&observe(newVal);部门通知();},});}functiondependArray(value){for(lete,i=0,l=value.length;i
