Vue.js

【Vue.js】 動的にコンポーネント切り替える方法

こんにちは!ともです(@_tomo_engineer)!

今回はVue.jsで動的にコンポーネントを入れ替える方法を見ていきます。

  • v-ifディレクティブを利用する方法
  • is属性を利用する方法
  • keep-aliveについて

について書きます。

参考にしたものは以下です。

基礎から学ぶVue.js(書籍)

Vue.js:日本語ドキュメント

動的コンポーネント

複数のコンポーネントがあり、ボタンにより表示するコンポーネントを切り替えるような、動的な動きをするコンポーネントを『動的コンポーネント』と呼びます。

これを実現するにはどのようにすれば良いのでしょうか?

v-ifディレクティブを利用する

v-ifディレクティブで何をshowするかで切り替える事ができます。

# コンポーネント
var myComponentA = {
    template: "<div>コンポーネントA</div>",
}
var myComponentB = {
    template: "<div>コンポーネントB</div>",
}

# Vueインスタンス
new Vue({
    el: '#app',
    components: {
        'my-component-a': myComponentA,
        'my-component-b': myComponentB,
    },
    data: {
        show: true
    },
    methods:{
        changeShow: function(){
            this.show = !this.show;
        }
    }
})
# HTML
<div id="app">
        <my-component-a v-if="show"></my-component-a>
        <my-component-b v-if="!show"></my-component-b>

        <button v-on:click="changeShow">toggle</button>
</div>

showというデータで切り替える事により描画させるコンポーネントを切り替えています。

しかしこれでは切り替えるコンポーネント分だけHTMLに追加しなければなりません。

component-aからcomponent-zまで存在するとHTMLが大きくなってしまいます。

is属性を利用する

is属性を利用すると、複数のコンポーネントを切り替える事ができます。

次のコードは基礎から学ぶVue.js(書籍)のP185ページから拝借しました。

# HTML
<div id="app">
        # is属性と一致するコンポーネントを描画する
    <div v-bind:is="component"></div>
        <button v-on:click="current^=1">toggle</button>
</div>
# 子コンポーネント
var myComponentA = {
    template: "<div>コンポーネントA</div>",
}
var myComponentB = {
    template: "<div>コンポーネントB</div>",
}

# Vueインスタンス
new Vue({
    el: '#app',
    components: {
        'my-component-a': myComponentA,
        'my-component-b': myComponentB,
    },
    data: {
        componentTypes: ['my-component-a', 'my-component-b'],
        current: 0,
    },
    computed:{
        component: function(){
    // 一致しているコンポーネント名を返す
            return this.componentTypes[this.current]
        }
    }
})

is属性を付与したdivタグが、コンポーネントに切り替わりました。

<div v-bind:is=”component-a”> => <component-a>

に変換されるようです。

v-ifで切り替えた場合と比べ、HTMLをスリムに記述できる事が出来ました。

2つ程度ならv-ifで切り替えても良いかもしれませんが、動的に切り替えるものが増える場合はis属性が便利だと感じました。

コンポーネントのライフサイクル

v-ifディレクティブで動的にコンポーネントを切り替えた場合、ライフサイクルは初期化されます。試しに次のように定義してみました。

# HTML, isで動的に切り替える
<div id="app">
        <div v-bind:is="component"></div>
        <button v-on:click="current^=1">toggle</button>
</div>

# 切り替えるコンポーネント
var myComponentA = {
    template: `<div>
    <p>Aを描画中</p>
    <input v-model='value'>
    </div>`,
    data: function(){
        return{
            value: 'Aのデータです。'
        }
    }
}
var myComponentB = {
    template: `<div>
    <p>Bを描画中</p>
    <input v-model='value'>
    </div>`,
    data: function(){
        return{
            value: 'Bのデータです。'
        }
    }
}

このようにコンポーネントにデータを持たせ、切り替えると次のようになりました。

このようにhogeを追加後にコンポーネントを切り替えると初期化されている事が分かります。

動的にコンポーネントを切り替えた場合は初期化されデータが残りません

動的に切り替えたい場合データを置いておきたい場合もあると思います。その場合はどうすれば良いのでしょうか。

keep-aliveで状態を維持する

動的にコンポーネントを切り替えた場合に初期化されるる事を防ぐには<keep-alive>が利用できます。

次のようにkeep-aliveを利用しました。

# HTML
<div id="app">
        <keep-alive>
            <div v-bind:is="component"></div>
        </keep-alive>
        <button v-on:click="current^=1">toggle</button>
</div>

動的に切り替わるコンポーネントを<keep-alive>で囲みました。これにより初期化される事を防ぐ事ができます。

動的に切り替えた後も、初期化されるデータが残っている事が分かります。

最後に

動的にコンポーネントを切り替える方法について書きました。

  • v-ifディレクティブでの切り替え
  • is属性での切り替え
  • keep-aliveにより初期化しないようにする

動的コンポーネントとライフサイクルを抑えて利用していきましょう。