昨天看了一些關於大量 DOM 的事情,今天來聊一下關於深度監看的事情。其實在 Vue 裡面,預設的監看並不會看太多,也就是不會看很深。為了效能考量,所以基本上各家前端框架在這件事情上面,或多或少都會留一點餘地。
然後這些餘地有時候會讓人覺得很困擾。
怎麼監看?
實際上 JavaScript 是可以透過 Proxy, Reflect 來實作一個簡易的監看方法。
const onChange = (objToWatch, callback) => {
const handler = {
get(target, property, receiver) {
callback();
const value = Reflect.get(target, property, receiver);
if (typeof value === 'object') {
return new Proxy(value, handler);
}
return value;
},
set(target, property, value) {
callback();
return Reflect.set(target, property, value);
},
deleteProperty(target, property) {
callback();
return Reflect.deleteProperty(target, property);
}
};
return new Proxy(objToWatch, handler);
};
他只會簡單告訴你你的物件有被更動,至於更動後要做的事情你得自己處理。撇除處理監看之間的事情,如果我們要持續觀察比較深度的物件,如果依據這種簡易的作法,你會發現一件事情:
const logger = () => console.log('onChange called.');
const obj = {
a: 123,
b: {
c: {
d: {
e: 456,
f: [ 7, 8, 9 ]
}
}
}
};
const proxyObj = onChange(obj, logger);
proxyObj.b.c.d.f = [ 1, 2, 3 ];
當你的深度越深,範圍越大的時候,你就很有可能會出現 Maximum call stack size exceeded
這一類的警告,大抵上就是你處理器寫壞掉了,如果可以的話,市面上也有一些關於 Watch 的工具,加減用一下應該會比較省事。
主要是讓你知道其實 監看 這件事情,在後面的程序上是相當耗費成本的。所以你也不能怪說 Vue 在 $watch
上面偷效能。這件事情如果弄不好其實也是會爆炸的,例如以前 Angular 1 的雙向綁定,弄不好就會掉到一個無窮迴圈裡面(但是這應該是人犯蠢跟 Watch 無關)。
至於大量資料的 Watch ,個人是相當不建議的,即便你的用戶端電腦效能超好,這種操作不小心還是會讓頁面當掉、無回應或是卡死的情況。如果真的想要監看什麼東西,建議還是把資料先耙梳過後,再來決定要監看的物件(甚至是做一次 Map/Reduce/總之看你要怎麼整理,之類的事情)。
真的要監看嗎?
通常我們會想要 $watch
一個物件、變數或是資料,多數狀況下是希望在 有更動 的時候,能夠通知 某個人 來去做 某一些事情。
但是,其實在 Vue 的生命週期中,你還有 updated
可以使用。顧名思義,就是有更新的時候就會呼叫這個勾子。只是,你需要搞清楚的地方是:
- 你的 變數 是否會觸發
updated
這勾子? - 當你的
updated
執行時,是否有其他動作需要處理? - 如果變數使用
$watch
,後續動作是否有觸發updated
勾子的可能性?
以上,如果你有機會觸發到 updated
的話,個人建議你把他放到 updated
去執行,不要用 $watch
來操作。
小結
如果你很在意效能,小心使用 $watch
,特別是你用了 { deep: true }
的時候。