1
理解:
-
創建視圖的函數(render)和數據之間的關聯;
-
當數據發生變化的時候,希望render重新執行;
-
監聽數據的讀取和修改;
- defineProperty:監聽范圍比較窄,只能通過屬性描述符去監聽已有屬性的讀取和賦值;兼容性更好;(要求監聽數據是對象)
- proxy:監聽范圍更廣;兼容性較差,只能兼容支持ES6 的瀏覽器(要求監聽數據是對象)
如何知曉數據對應的函數;
function track(target, key) {console.log(`依賴收集:${key}`, 'color: #f00');
}function trigger(target, key) {console.log(`派發更新:${key}`, 'color: #f00');
}function isObject(value) {return typeof value === 'object' && value !== null;
}
const handlers = {get(target, key) {// 依賴收集track(target, key);return target[key]; // 返回對象的相應屬性值},set(target, key, value) {// 派發更新trigger(target, key);// target[key] = value; // 設置對象的相應屬性值// return true;// 賦值成功返回true,賦值失敗返回false;這里可以使用try catchreturn Reflect.set(target, key, value)// 也可以使用Reflect.set(target, key, value)。它會直接返回true或者false},
}
// 同一個對象,調用兩次reactive,會生成不一樣的Proxy對象,沒有意義
const targetMap = new WeakMap();
function reactive(target) {if (!isObject(target)) {return target; // 如果不是對象,直接返回}if (targetMap.has(target)) {return targetMap.get(target);// 如果已經代理過了,直接返回;}const proxy = new Proxy(target, handlers);targetMap.set(target, proxy);return proxy;
}
const state = reactive({a: 1,b: 2,
});// fn函數中用到了state數據
function fn() {state.a;state.b;
}fn();
state.a++; // 先讀取,再賦值
依賴收集:a color: #f00
依賴收集:b color: #f00
依賴收集:a color: #f00
派發更新:a color: #f00
const obj = {a: 1,b: 2,get c() {return this.a + this.b;}
};const state = reactive(obj);
state.c;
這樣寫的話,依賴收集只能收集到屬性c;因為this指向obj;
可以這樣操作:
get(target, key, receiver) {// 依賴收集track(target, key);// receiver指的是代理對象return Reflect.get(target, key, receiver) ; // 改變this指向,將this指向為代理對象// return target[key]; // 返回對象的相應屬性值
},
下面的用法,只能收集到c,收集不到c1
const obj = {a: 1,b: 2,c: {c1: 1,},
};const state = reactive(obj);
state.c.c1;
可以這樣操作
如果訪問的屬性值還是一個對象,對屬性值再次進行代理;
const obj = {
includes: () => {},
indexOf: () => {},
};
const handlers = {get(target, key, receiver) {// 依賴收集track(target, key);// 對于數組來說,無法在代理對象中找到時,去原始數組中重新找一次// const obj = {};// const arr = [1, obj, 3];// const state = reactive(arr);// state.includes(obj);if ((obj.hasOwnProperty(key) && Array.isArray(target)) {return obj[key];}// receiver指的是代理對象const result = Reflect.get(target, key, receiver) ; if (isObject(result)) {return reactive(result);}},
}
簡易的模型已經寫好;
2 讀信息 進行依賴升級
Object.keys和let i in obj用的都是ownKeys;
‘a’ in obj; 用的是has;
obj.a 用的是get;
這里的讀不光是通過state.a來讀取a屬性
還可能通過’e’ in state;來查看’e’屬性在不在state中;
const obj = {};
const state = reactive(obj);
'e' in state;
解決辦法:新增has方法
const TrackOpTypes = {GET: 'get', // 讀取屬性值HAS: 'has', // 判斷屬性是否存在ITERATE: 'iterate', // 迭代對象
};function track(target, trackOpType, key) {console.log(`依賴收集:${key},收集方法:${trackOpType}`, 'color: #f00');
}
const handlers = {get(target, key, receiver) {// 依賴收集track(target, TrackOpTypes.GET, key);// ...},set(target, key, value, receiver) {},has(target, key) {track(target, TrackOpTypes.HAS, key);return Reflect.has(target, key); // 判斷對象是否有key屬性}
}
同理
const TriggerOpTypes = {SET: 'set', // 設置屬性值ADD: 'add', // 添加屬性值DELETE: 'delete', // 刪除屬性
}function trigger(target, triggerOpType, key) {console.log(`派發更新:${key}, 更改方法:${triggerOpType}`, 'color: #f00');
}
還有一種情況
const handlers = {get,set,has,ownKeys(target) {track(target, TrackOpTypes.ITERATE);return Reflect.ownKeys(target); // 返回對象的所有屬性名},
}
function track(target, trackOpType, key) {if (trackOpType === TrackOpTypes.ITERATE) {console.log(`依賴收集方法:${trackOpType}`, 'color: #f00');return;}console.log(`依賴收集:${key},收集方法:${trackOpType}`, 'color: #f00');
}
const obj = { a: '1'};
const obj2 = {}
const state = reactive(obj);
const state2 = reactive(obj2);
for (let i in state) {}
Object.keys(state2);
Object.keys和let i in obj用的都是ownKeys;
依賴收集方法:iterate color: #f00
依賴收集方法:iterate color: #f00
3 新增屬性
set(target, key, value, receiver) {// 派發更新const type = target.hasOwnProperty(key)? TriggerOpTypes.SET: TriggerOpTypes.ADD;trigger(target, type, key);return Reflect.set(target, key, value, receiver);},
4 刪除屬性
deleteProperty(target, key) {trigger(target, TriggerOpTypes.DELETE, key);return Reflect.deleteProperty(target, key); // 刪除對象的相應屬性}
delete state.a;