[IT 鐵人賽] 大型資料載入實例與狀況 Deep watch Day 27

昨天看了一些關於大量 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 可以使用。顧名思義,就是有更新的時候就會呼叫這個勾子。只是,你需要搞清楚的地方是:

  1. 你的 變數 是否會觸發 updated 這勾子?
  2. 當你的 updated 執行時,是否有其他動作需要處理?
  3. 如果變數使用 $watch ,後續動作是否有觸發 updated 勾子的可能性?

以上,如果你有機會觸發到 updated 的話,個人建議你把他放到 updated 去執行,不要用 $watch 來操作。

我絕對沒有要嘴 Vue 的意思


小結

如果你很在意效能,小心使用 $watch ,特別是你用了 { deep: true } 的時候。

ITHome 鐵人賽同步刊登 大型資料載入實例與狀況 Deep watch Day 27

Hina Chen
偏執與強迫症的患者,算不上是無可救藥,只是我已經遇上我的良醫了。
Taipei