Vue變化檢測Object使用DefineProperty、數組使用方法攔截實現。最近,Vue3.0將采用ES6 Proxy的形式重新實現Vue的變化檢測,在官方還沒給出新方法之前,我們先實現一個基于Proxy的變化檢測。
模塊劃分
參照之前Vue變化檢測的代碼,將Vue 變化檢測的功能分為以下幾個部分。
Observer Dep Watcher Utils首先,我們要確定的問題是,將Dep依賴搜集存在哪里。Vue 2.x里,Object的依賴收集放在defineRactive,Array的依收集存入到Observer中。ES6 Proxy里,考慮到讓handler訪問dep,我們將依賴放入到Observer中。
Observer
observer.js功能代碼如下:
import Dep from './dep';import { isObject } from './utils';export default class Observer { constructor (value) { // 遞歸處理子元素 this.obeserve(value); // 實現當前元素的代理 this.value = this.proxyTarget(value); } proxyTarget (targetBefore, keyBefore) { const dep = new Dep(); targetBefore.__dep__ = dep; let self = this; const filtersAtrr = val => ['__dep__', '__parent__'].indexOf(val) > -1; return new Proxy(targetBefore, { get: function(target, key, receiver){ if (filtersAtrr(key)) return Reflect.get(target, key, receiver); if (!Array.isArray(target)) { dep.depend(key); } // sort/reverse等不改變數組長度的,在get里觸發 if (Array.isArray(target)) { if ((key === 'sort' || key === 'reverse') && target.__parent__) { target.__parent__.__dep__.notify(keyBefore); } } return Reflect.get(target, key, receiver); }, set: function(target, key, value, receiver){ if (filtersAtrr(key)) return Reflect.set(target, key, value, receiver); // 新增元素,需要proxy const { newValue, isChanged } = self.addProxyTarget(value, target, key, self); // 設置key為新元素 Reflect.set(target, key, newValue, receiver); // notify self.depNotify(target, key, keyBefore, dep, isChanged); return true; }, }); } addProxyTarget(value, target, key, self) { let newValue = value; let isChanged = false; if (isObject(value) && !value.__parent__) { self.obeserve(newValue); newValue = self.proxyTarget(newValue, key); newValue.__parent__ = target; isChanged = true; } return { newValue, isChanged, } } depNotify(target, key, keyBefore, dep, isChanged) { if (isChanged && target.__parent__) { target.__parent__.__dep__.notify(keyBefore); return; } if (Array.isArray(target)) { if (key === 'length' && target.__parent__) { target.__parent__.__dep__.notify(keyBefore); } } else { dep.notify(key); } } obeserve(target) { // 只處理對象類型,包括數組、對象 if (!isObject(target)) return; for (let key in target) { if (isObject(target[key]) && target[key] !== null) { this.obeserve(target[key]); target[key] = this.proxyTarget(target[key], key); // 設置__parent__,方便子元素調用 target[key].__parent__ = target; } } }}
新聞熱點
疑難解答
圖片精選