Vue.js で特定のプロパティを変更の追跡の対象外にする

きっかけ

とある案件で、とあるサードパーティSDK を利用していた。その SDK が提供するコンストラクタから生成したインスタンスを Vue.js のインスタンスのデータとして扱おうとしたら、Security Error のような例外が投げられて困った。 (回避するために、インスタンスのデータとして扱わないように試みたのだけど、致し方ない理由で諦めた)

原因

Vue.js では、データの変更を追跡するためにプロパティを getter/setter に上書きする。ココらへんの話は リアクティブの探求 — Vue.js が詳しい。この上書きの処理はプロパティがオブジェクトの場合は掘り下げて再帰的に行われる。おそらく問題の起きたインスタンスでは、セキュリティの観点からこういった処理を防ぐ機構が入っていたのだと推測。すごいね。

解決

これまた公式ドキュメントなのだけど、Object.freeze で追跡の対象外にできることを知ったので以下のようなコードを書いた( Vue インスタンス — Vue.js )。

const foo = new ThirdpartyFoo();

const container = {
  foo,
};

const vm = new Vue({
  data() {
    return {
      container: Object.freeze(contianer),
    },
  },
});

vm.container.foo;

はじめ foo 自体を Object.freeze したのだけど、foo のメソッド呼び出しに伴うプロパティの変更ができなくなってしまった。そこで、容れ物のオブジェクトを用意して、元のオブジェクトを包むことにした。こうすることで、foo のプロパティの getter/setter への上書きは防ぎつつ、これまで通り foo のメソッドを呼び出すことができる。

これまで特に意識せず、様々なオブジェクトを Vue のインスタンスのデータとして扱っていたのだけど、変更を追跡する必要がない、オブジェクトのプロパティの階層が深い( getter/setter への上書きのコストが高い )オブジェクトは Object.freeze で追跡の対象外にしてよいかもしれない。