😏

[Vue.js] コンポーネントの再描画の方法3選

2023/11/29に公開

リアクティブなVue

あるデータを変更すると、それに関連するデータも合わせて変化し画面の表示(DOM)も自動更新される。 この仕組みがVueの最大の特徴とも言えるリアクティブ性です。

ただ開発をしていると、ユーザー操作に対応するためにコンポーネントの再描画を行い最新の状態に保つ必要があったり、propsや外部から取得している値が更新される前にDOMが更新されてしまい最新のデータが表示されないといった問題に遭遇したりすることもあるかと思います。

そんな時にコンポーネントを再描画する方法をアンチパターンを含めて3つご紹介します。

v-ifを使った再描画 (よろしくない)

まず一つ目はv-ifディレクティブを使用し、条件に応じて表示を切り替える方法です。

下記コードでは、isRenderedの値がtrueの場合にChildコンポーネントがレンダリングされるようになっています。再描画ボタンをクリックすることでupdateComponent関数が呼ばれますが、関数内ではisRenderedの値を一度falseにしDOMの更新が終わった後にtrueに戻すということを行なっています。
そうすることで、VueがChildコンポーネントを再描画するという仕組みです。

Parent.vue
<template>
  <Child v-if="isRendered" />
  <button @click="updateComponent">再描画</button>
</template>

<script setup>
import Child from './Child.vue'
import { nextTick, ref } from 'vue'

const isRendered = ref(true)
const updateComponent = async () => {
  isRendered.value = false
  await nextTick()
  isRendered.value = true
}
</script>

確かにChildコンポーネントは再描画されますが、この方法ではDOMが2回更新されることになってしまいパフォーマンス上好ましくない、無理矢理な方法です。
よって避けるべきです。

forceUpdateを使った再描画 (良い)

お次は$forceUpdateメソッドを使用した方法です。Vueの公式でもサポートされている方法で、強制的にコンポーネントの再描画を行うことができます。
Vue2ではリアクティブな変更を検知できない特定のケースなどがありましたが、Vue3では解消されているためあまり使用される場面はないと公式では紹介されています。(実際自身も使用したことはないです)

forceUpdate.vue
<script setup>
import { getCurrentInstance } from 'vue';

const methodThatForcesUpdate = () => {
  // ...
  const instance = getCurrentInstance();
  instance.proxy.forceUpdate();
  // ...
};
</script>

:keyを使った再描画 (BEST!!!)

最後に一番推奨されている:keyを使用した方法です。
key属性はkey属性に渡している値が変化する度にコンポーネントを再描画してくれます。

特別な属性 key は、主に Vue の仮想 DOM アルゴリズムが新しいノードリストを古いリストに対して差分する際に、vnode を識別するためのヒントとして使用されます。

上記は公式ドキュメントでのkey属性についての説明ですが、Vueはkeyが変更される度にコンポーネントを破棄し新しく生成し描画するという処理を行なっているのでkeyの変更に応じてコンポーネントの再描画が行われます。

下記はv-ifを使用した場合のコードを書き換えて:key属性を使用して実現したコードです。

Parent.vue
<template>
  <Child :key="renderKey" />
  <button @click="updateComponent">再描画</button>
</template>

<script setup>
import { ref } from 'vue'
import Child from './Child.vue'

const renderKey = ref(0)
const updateComponent = () => {
 renderKey.value = renderKey.value + 1
}
</script>

Childコンポーネントにはv-ifではなく:keyを付与しています。
再描画ボタンを押すことで:keyに渡しているrenderKeyが更新され、
その更新をトリガーにChildコンポーネントが再描画されるようになります。

まとめ

コンポーネントの再描画を行いたい場合は
v-if$forceUpdateを使用した方法と比べて:keyを付与するだけという簡単かつ
一部のコンポーネントのみを再レンダリングができる:keyを使うのが良さそうです!

参考文献

https://www.webmound.com/force-re-render-vue-components/
https://michaelnthiessen.com/force-re-render
https://ja.vuejs.org/api/component-instance.html#forceupdate

Discussion