5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VueAdvent Calendar 2024

Day 3

[Vue.js]refのオブジェクトにプロパティ追加した時にwatchが走らない?

Last updated at Posted at 2024-12-02

vueアドベントカレンダー3日目です!

サンプル

ボタンをクリックすると動的にプロパティを追加するコードがあるとします

<script setup>
import { watch, ref } from 'vue'

const refObj = ref({})

function add() {
  refObj.value["add"] = "hello"
}

// addしても動かない
watch(refObj, () => {
  console.log("watch ref", refObj.value)
})
</script>

<template>
  <div>{{refObj}}</div>
  <button @click="add">add</button>
</template>

このwatchはプロパティ追加時には動作しません

deep:trueをつければwatchが動きます

watch(refObj, () => {
  console.log("watch ref", refObj.value)
}, {deep: true})

もしくは、reactiveにしてもプロパティ追加をwatchできます

const reactiveObj = reactive({})

function add() {
  reactiveObj["add"] = "hello"
}

watch(reactiveObj, () => {
  console.log("watch reactiveObj", reactiveObj)
})

なぜ? :thinking:

watchのソースを読みます

watchの対象がrefのとき

このgetterを監視してcallbackを呼ぶようです

.valueが変わらないのでプロパティ追加してもcallbackは呼ばれません

  if (isRef(source)) {
    getter = () => source.value

つまりこのように.valueに新しいobjを入れれば動作します

function add() {
  refObj.value["add"] = "hello"
  // valueが変わるのでwatchが動く
  refObj.value = {...refObj.value}
}

deep=trueの場合

traverseで再帰的にプロパティを見ているので追加してもわかります

  if (cb && deep) {
    const baseGetter = getter
    const depth = deep === true ? Infinity : deep
    getter = () => traverse(baseGetter(), depth)
  }

そしてcallbackを呼ぶ条件に、deepがあるのでcallbackが呼ばれます

      if (
        deep ||
        forceTrigger ||
        (isMultiSource
          ? (newValue as any[]).some((v, i) => hasChanged(v, oldValue[i]))
          : hasChanged(newValue, oldValue))
      ) {

reactiveのとき

こちらはreactiveのときの条件です
reactiveGetterの中でtraverseしています

  } else if (isReactive(source)) {
    getter = () => reactiveGetter(source)
    forceTrigger = true

そして先程のcallbackを呼ぶ箇所で、forceTrigger=trueなのでcallbackが呼ばれます

      if (
        deep ||
        forceTrigger ||
        (isMultiSource
          ? (newValue as any[]).some((v, i) => hasChanged(v, oldValue[i]))
          : hasChanged(newValue, oldValue))
      ) {

おわり

動作は変わる可能性もあるので注意して下さい :astonished:

明日は @kosuke-naito さんです!

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?