Vue.js 子コンポーネントを強制的に再描画するいくつかの方法
motivation
子コンポーネントを再描画したい。いくつかのやり方を見つけたのでメモ。
公式の注意書きでは、以下のように書いてあります。
もし Vue で強制更新をする必要な場面に遭遇する場合、99.99% のケースであなたは何かを間違えています。
しかし私の場合は、特定のライブラリが更新されないという特異的な状況に出会ったので、子コンポーネントを強制的に再描画させる方法を模索しました。
サンプルリポジトリです。今回、紹介している各方法のコードを載せています。
github.com
prerequisites
環境についてです。
versions
- vue: 2.6.10
- core-js: 2.6.5
components
再描画したい子コンポーネントです。
▼ Child.vue
<template> <div> <p>{{ count }}</p> <button @click="increment">+ 1</button> </div> </template> <script> export default { name: "Child", data() { return { count: 0 }; }, methods: { increment() { this.count++; } } }; </script>
solutions
3つの方法を見つけましたので、順に紹介。
v-if
v-if
を利用すると、再計算されるようです。単純に、true と false の切り替えだと、再計算されなくなるかもなので、nextTick
を利用して、DOMの更新サイクル後に、子コンポーネントを再計算させます。
<template> <div> <button @click="toggle">rerender by v-if</button> <child v-if="showChild"></child> </div> </template> <script> import Child from "./Child.vue"; export default { name: "Vif", components: { Child }, data() { return { showChild: true }; }, methods: { toggle() { this.showChild = false; this.$nextTick(() => (this.showChild = true)); } } }; </script>
key
v-for
を利用したときによく使う key
ですが、普通のコンポーネントにも利用できるみたいです。key
を更新することで、前のkeyのDOMが破棄/削除され、同じ場所に新しいkey
の子コンポーネントが生まれる?
<template> <div> <button @click="toggle">rerender by key</button> <child :key="key"></child> </div> </template> <script> import Child from "./Child.vue"; export default { name: "Key", components: { Child }, data() { return { key: 0 }; }, methods: { toggle() { this.key = this.key ? 0 : 1; } } }; </script>
instance
最後は、JavaScriptでごりごり書く方法。最初にtextContent = null
を使うことで、前の子コンポーネントを削除。そのあとに、子コンポーネントのインスタンスを生成させて、appendChild()
でDOMに追加という感じ。
見た目で他の再描画の方法より再描画している感が出ているので、分かりやすさはある。
<template> <div> <button @click="toggle">rerender by instance</button> <div ref="parent"> <child></child> </div> </div> </template> <script> import Vue from "vue"; import Child from "./Child.vue"; export default { name: "Instance", components: { Child }, methods: { toggle() { this.$refs["parent"].textContent = null; const ComponentClass = Vue.extend(Child); const instance = new ComponentClass(); instance.$mount(); this.$refs["parent"].appendChild(instance.$el); } } }; </script>
この場合で、子コンポーネントにemitがあり、それを親で購読したい場合は以下を参照。 tomatoaiu.hatenablog.com
conclusion
子コンポーネントを再描画したいときに利用できる3つの方法(v-if
, key
, instance
)を書きました。どうしても再描画が必要な特殊な処理を書く場合には、いずれかの方法を利用することで問題を解決できそうです。他にも、forceUpdate
を呼ぶ方法が利用できそうですが要調査。
大抵の場合には、これらの方法は利用しないと思いますが、頭の片隅に置いておくといつか使えるかもしれません。
私の場合は、instanceを生成する方法で再描画されない問題を対処しました。きっともう使うことはないでしょう……。
references
- The correct way to force Vue to re-render a component - Michael Thiessen, 入手先 https://michaelnthiessen.com/force-re-render/
- Creating Vue.js Component Instances Programmatically | CSS-Tricks, 入手先 https://css-tricks.com/creating-vue-js-component-instances-programmatically/
- 作者:MIO
- 発売日: 2018/05/29
- メディア: Kindle版