Vue公式ガイド読解メモ-リアクティブの探求

本日は「リアクティブの探求」を読みます。

jp.vuejs.org

Vueの肝の部分かと思うので、これは興味あり。 全部は理解できないと思いますが、深掘って行きたいと思います。

  • Vue最大の特徴の一つは「控えめなリアクティブシステム」
  • モデルは単なるプレーンなJavaScriptオブジェクト

ほう。これが肝なのかな。ただのオブジェクト(何かを継承しているわけではない)なのに、リアクティブに反応できる仕組みがあるんですね。

変更の追跡方法

  • dataオプションをVueインスタンスにわたすと、全てプロパティをObject.definePropertyを使用してgetter/setterに変換する
  • ES5以上で使える機能でshimできないので、VueはIE8以下をサポートしていない

IE9でもdefineProperty使えるんか。

dataのオブジェクトにgetter/setterを追加して、setしたときに変更を通知したりとか、そういう機能を追加しているってことだろうな。

developer.mozilla.org

ふーん。dataオブジェクトにgetterを追加してて、コンポーネントを描画するときに、getterが呼ばれるから、getter呼ばれたプロパティを記録してると、で、記録したプロパティがsetterで更新されたら、watcherに通知が行くと。

なんとなくわかったような。

f:id:tropical_eng83:20220218095530p:plain

変更検出の注意事項

  • JSの制限のため、Vueは検出することができない変更のタイプがある
  • でもこれらは回避できる

ほう、どんなタイプ?

オブジェクトに関して

  • Vueはプロパティの追加・削除を検出できない
  • Vueはインスタンスの初期化中にgetter/setterの変換するため、dataオブジェクトに初期化時に存在している必要がある
  • ネストしたオブジェクにリアクティブなプロパティを追加することはできる
// dataにすでにsomeObjectが追加されており、そのプロパティとしてbを追加する場合

//グローバルAPI
Vue.set(vm.someObject, 'b', 2)

//エイリアス
//vmインスタンス内で
this.$set(this.someObject, 'b', 2)

既存のオブジェクトに複数のプロパティを割り当てたいということがあるかもしれません。例えば、Object.assign() や _.extend() を使って。しかし、オブジェクトに追加された新しいプロパティは変更をトリガしません。このような場合は、元のオブジェクトとミックスインオブジェクトの両方のプロパティを持つ新たなオブジェクトを作成してください:

ん?、これよくわかんない。リアクティブにならなくないか?

リアクティブになっている、this.someObjectの参照を新しいオブジェクトで置き換えているだけで、Vue.set()とかもするわけでないし。 これでリアクティブになるなら、他の新規オブジェクトを追加してもリアクティブになる気がする。。

なぞ。

配列に関して

  • インデックスと一緒にアイテムを直接セットする場合は検知できない
  • 配列の長さを変更する場合は検知できない。
// インデックスと一緒にアイテムセット
vm.items[indexOfitem] = newValue

// 長さの変更
vm.items.length = newLength

回避方法

Vue.set(vm.items, indexOfItem, newValue)
// なんかこれ、リアクティブにできるように配列用のロジックがsetに組み込まれてそうだな

// splice経由で更新する
vm.items.splice(indexOfItem, 1, newValue)


// エイリアスもある
// 省略、vm$set(), vm.items.splice()

配列操作って、結構やる気がするけど、これは罠だな。

ちなみに、配列のlengthを直接代入すると、配列が短くなったり、拡張されたりするっぽかった。(これも知らなかった)

リアクティブのプロパティ宣言

  • リアクティブなプロパティを動的に追加できないので、初期化時に全てのルートレベルのリアクティブなdataプロパティを宣言する必要がある。
  • 空でも良い

空でも良いがプロパティを追加しておく理由

技術的観点

  • 依存性対生システムの一連のエッジケースを排除する
    • エッジケース、なんだろ、特定条件で発生する問題を回避するって感じか
  • Vueインスタンスと型チェックシステムとの親和性を高める
    • プロパティがあった方が、型定義として利用できそうな気がする(たしかに)

保守性の観点

  • dataオブジェクトはコンポーネントの状態のスキーマのようなもの
  • リアクティブなプロパティを宣言しておくと、後から見るときにコードの理解がはかどる

ほー。技術的観点のエッジケースはよくわからんが、保守性の観点はわかる。どんなプロパティが存在するのか、リアクティブなのはここ見ればわかる、というのはコード読むときは嬉しいかな。

非同期更新キュー

この非同期更新キュー、結構概念的に重要そうだ。

  • Vueは非同期にDOM更新を実行する
  • データ更新が検出すると、キューをオープンし同じイベントループで起こる全てのデータ変更をバッファリングする
  • ウォッチャが複数回トリガされても、キューには1度だけ入る
    • キューに1度なので、DOM更新がまとめて行われる、不要な計算が回避できる
  • イベントループ(tick)でキューをフラッシュ、DOM操作をする
  • vm.someData = 'new value'をセットしたとき、コンポーネントはすぐに再描画されない
  • 次のtickでキューがフラッシュされるときに更新する
  • データの変更後にVue.jsのDOM更新完了をまつにはVue.nextTick(callback)を使う
  • callbackはDOMが更新された後に呼ばれる
    updateMessage: function () {
      this.message = 'updated'
      console.log(this.$el.textContent) // => 'not updated'
      this.$nextTick(function () {
        console.log(this.$el.textContent) // => 'updated'
      })
    }

なるほど、nextTickがなんなのか、ようやく理解できた。 キュー?イベントループを1つ進めることをtickと呼んでいるのね。nextTickは実質はキューを処理した後に実行するcallbackの指定か。(名前がいまいちな気もするが)

やはり、新しい発見がありましたね。

今日はこれで終わり。

次は「移行」の章だけど、そこは一旦いいかな。

少し飛ばして「他のフレームワークとの比較」を読もうと思います。 それが終わったら、Vueの公式スタイルガイド読もうっと。