1
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?

More than 3 years have passed since last update.

Vue.jsのwatchが動かない謎を解け

Posted at

問題

早速ですが以下のコードを見てください。
カウントアップボタンを1回クリックした際に、最初に出力される数字は何でしょう?
出力している部分はcomponents/Counter.vueの①の部分です。

(A) 0
(B) 1
(C) 2
(D) 何も出ない

pages/index.vue
<template>
  <div>
    <counter v-if="counter > 0" />
    <button type="button" @click="increment">カウントアップ</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, inject, provide } from "@nuxtjs/composition-api";
import useCounterStore, { CounterStore, CounterStoreKey } from "~/stores/CounterStore";
import Counter from "~/components/Counter.vue";

export default defineComponent({
  components: { Counter },
  setup() {
    provide(CounterStoreKey, useCounterStore());

    const { counter } = inject(CounterStoreKey) as CounterStore;

        const increment = () => counter.value++;

    return {
      counter,
      increment
    };
  },
});
</script>
components/Counter.vue
<template>
  <div>{{ counter }}</div>
</template>

<script lang="ts">
import { defineComponent, inject, watch } from "@nuxtjs/composition-api";
import { CounterStore, CounterStoreKey } from "~/stores/CounterStore";

export default defineComponent({
  setup() {
    const { counter } = inject(CounterStoreKey) as CounterStore;

    watch(counter, () => console.log(counter.value)); // ①

    return {
      counter,
    };
  },
});
</script>
stores/CounterStore.ts
import { InjectionKey, ref } from "@nuxtjs/composition-api"

const useCounterStore = () => {
  const counter = ref(0)
  return { counter }
}

export type CounterStore = ReturnType<typeof useCounterStore>
export const CounterStoreKey: InjectionKey<CounterStore> = Symbol('CounterStore')
export default useCounterStore

正解

(D)の「何も出ない」です。

理由

この問題のカギとなるのは、v-ifです。
pages/index.vueで、components/Counter.vueの表示に、v-ifを利用しています。
Vue.jsを使ったことがある人だったらわかると思いますが、v-ifは特定の条件の場合のみ表示するためのディレクティブです。

今回の場合、counter > 0の場合に表示されるようになっているので、初期表示時はcounter = 0なので表示されないようになっています。さらに、v-ifはHTMLタグ自体も出力しないため、Counterコンポーネントがページ上にない状態です。つまり、watchも動作しないため、0 → 1の変更の検知はできないということになります。

最初のカウントアップで、counterが1となり、Counterコンポーネント が表示されwatchが設定されるようになり、以降、カウンターの変更を検知しwatchで指定した関数が実行されます。

どうすればよいか

v-if ではなく、v-showを使うことで、問題を解決できます。v-showはHTML自体は出力され、CSSのdisplayで表示切り替えを行います。Counterコンポーネント自体は出力されているので、画面上見えてなくても、watchは動くようになります。

1
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
1
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?