
前回の記事では、<component>要素を使用して、動的にコンポーネントを切り替える方法について紹介しました。
動的にコンポーネントを切り替えることで、一定時間で表示内容が変化するバナーや、複数のページを切り替えることができるタブを持つUIやなどを簡単に実装することができるようになります。

前回の記事では、タブを例に動的なコンポーネントを切り替えて表示する例を示しました。
コンポーネントの切り替えには<component>要素を利用し、is属性にインポートするコンポーネントを指定することで動的にコンポーネントを切り替えることができるようになります。
今回の記事では、前回と同様にタブを持つUIを使用して、動的に切り替わるコンポーネントの状態を保持する際に利用する<keep-alive>要素について紹介します。
目次
基本となる実装 – サンプルコード
サンプルコードとして、タブを持つUIを実装します。
ここでは、前回のタブを持つアプリケーションを少し変更して以下のような実装を行います。
HTML
| 
					 1 2 3 4 5 6 7 8 9 10 11 12  | 
						<div id="app">   <!-- タブのボタン -->   <button      v-for="tab in tabs"      v-bind:key="tab.key"      v-bind:class="['tab-button', { active: currentTab === tab.key }]"      v-on:click="currentTab = tab.key">     {{ tab.name }}   </button>   <!-- タブページのコンポーネント -->   <component v-bind:is="currentTabComponent"></component> </div>  | 
					
CSS
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  | 
						.tab-button {   padding: 6px 10px;   border-top-left-radius: 3px;   border-top-right-radius: 3px;   border: 1px solid #ccc;   cursor: pointer;   background: #f0f0f0;   margin-bottom: -1px;   margin-right: -1px; } .tab-button:hover {   background: #e0e0e0; } .tab-button.active {   background: #e0e0e0; } .tab-page {   border: 1px solid #ccc;   padding: 10px; }  | 
					
JavaScript
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88  | 
						// ホームタブのコンポーネント const homeTab = {   template: ` <div class="tab-page">   Welcome to JOHOBASE, a blog that introduces Vue.js introductory and tips. </div>` }; // リファレンスタブのコンポーネント const referenceTab = {   template: ` <div class="tab-page">   <input type="text" v-model="searchText" />   <button v-on:click="onClick">検索</button> </div>`,   data: function() {     return {       searchText: ''     }   },   methods: {     onClick: function() {       // ここに検索実行時の処理を記述する...     }   } }; // ドキュメントタブのコンポーネント const documentTab = {   template: ` <div class="tab-page">   <input type="radio" name="category" id="vue" value="vue" v-model="checked" v-on:change="onChange" />   <label for="vue">Vue</label>   <br>   <input type="radio" name="category" id="html" value="html" v-model="checked" v-on:change="onChange" />   <label for="html">HTML</label>   <br>   <input type="radio" name="category" id="js" value="js" v-model="checked" v-on:change="onChange" />   <label for="js">JavaScript</label>   <br>   <input type="radio" name="category" id="css" value="css" v-model="checked" v-on:change="onChange" />   <label for="css">CSS</label> </div>`,   data: function() {     return {       checked: ''     }   },   methods: {     onChange: function() {       // ここにカテゴリー変更時の処理を記述する...     }   } }; // アプリケーションのVueインスタンス const app = Vue.createApp({   data() {     return {       currentTab: 'Home',       tabs: [         {           key: 'home',           name: 'ホーム'         },         {           key: 'reference',           name: 'リファレンス'         },         {           key: 'document',           name: 'ドキュメント'         }       ]     }   },   computed: {     currentTabComponent() {       return 'tab-' + this.currentTab.toLowerCase()     }   } }) // コンポーネントの設定 app.component('tab-home', homeTab) app.component('tab-reference', referenceTab) app.component('tab-document', documentTab) // HTMLテンプレートとのマウント設定 app.mount('#app')  | 
					
上記のコードを実行すると、次のように3つのタブを持つ画面が表示されます。
ホームタブ

リファレンスタブ

ドキュメントタブ

ホームタブには、メッセージのテキストを表示しているだけですが、リファレンスタブとドキュメントタブには、データを入力(選択)するためのフォーム入力要素(コントロール)を配置しています。
サンプルコードの実行
サンプルコードを実行して、リファレンスタブの検索テキストボックスにテキストを入力するか、ドキュメントタブのカテゴリーのラジオボタンをクリックして項目を選択します。
ここでは、リファレンスタブの検索テキストボックスにテキストを入力します。

テキストを入力したら、いったん別のタブ選択して、再度リファレンスタブを選択します。
すると、入力していた値が消去され、テキストボックスには何も入力されていない状態になることが確認できます。

これは、新しいタブに切り替えるたびに、Vueが<component>要素で切り替えているコンポーネントの新しいインスタンスを作成するからです。
コンポーネントの切り替えが発生する度に、新しいインスタンスを作成して初期化する挙動は便利ですが、今回のサンプルでは、コンポーネントが切り替わっても元の状態が保持されている方が好ましいです。
コンポーネントの状態を保持(キャッシュ)する<keep-alive>要素
動的に切り替えるコンポーネントの状態を保持するためには、動的コンポーネント(<component>要素)を<keep-alive>要素で囲みます。
| 
					 1 2 3 4  | 
						<!-- タブページのコンポーネント --> <keep-alive>   <component v-bind:is="currentTabComponent"></component> </keep-alive>  | 
					
<component>要素を<keep-alive>要素で囲んで実装コードを実行し、先ほどと同様にリファレンスタブの検索テキストボックスに値を入力してタブを移動すると、今度はテキストボックスに入力した値が保持されているのが確認できます。
<keep-alive>要素を使うと、動的なコンポーネントの切り替え時に、コンポーネントの状態をキャッシュに.0保持してくれますので、上記のようなフォーム入力がある場合だけではなく、パフォーマンスの理由から再レンダリングを避けたい場合にも利用することができます。
特にコンポーネント内に配置されている要素が多く、コンポーネントが切り替わるたびに再レンダリングされたくない場合は、とても有効な手段となります。
<keep-alive>要素を使用する際の注意点
<keep-alive>要素で<component>要素を囲む場合には、<keep-alive>要素の直下に<component>要素が配置されるようにしてください。
<keep-alive>要素と<component>要素の間にコメント(<!– –>)などのタグがあると、コンポーネントのキャッシュが行われなくなりますので注意が必要です。
| 
					 1 2 3 4  | 
						<keep-alive>   <!-- タブページのコンポーネント -->   <component v-bind:is="currentTabComponent"></component> </keep-alive>  | 
					
上記のように<keep-alive>要素と<component>要素の間にコメントを記述すると、コンポーネントのキャッシュが行われないことが確認できます。
keep-alive要素の属性
</keep-alive>では、コンポーネントをキャッシュする方法を制御するために、いくつかの属性が用意されています。
属性を指定することで、キャッシュを行うコンポーネントを指定したり、効率的にキャッシュを管理できるようになります。
max属性
max属性は、キャッシュを行うコンポーネントの最大数を指定します。
キャッシュの個数が指定された値を超えた場合は、その時点で直近で一番アクセスされていないコンポーネントから破棄されていきます。
| 
					 1 2 3  | 
						<keep-alive v-bind:max="3">   <component>...</component> <keep-alive>  | 
					
上記の例では、キャッシュの個数を3に指定しています。
include、exclude属性
include属性は、キャッシュすべきコンポーネントを指定する属性になります。
exclude属性は、include属性とは逆に、キャッシュすべきでないコンポーネントを指定する属性になります。
include、exclude属性では対象となるコンポーネントの名前を
- カンマ区切りの文字列
 - 配列
 - 正規表現
 
の3つの方法のいずれかで指定します。
カンマ区切り文字列
| 
					 1 2 3 4 5 6 7 8 9  | 
						<!-- include --> <keep-alive include="comp1,comp2">   <component>...</component> <keep-alive> <!-- exclude --> <keep-alive exclude="comp3,comp4">   <component>...</component> <keep-alive>  | 
					
配列
| 
					 1 2 3 4 5 6 7 8 9  | 
						<!-- include --> <keep-alive v-bind:include="['comp1','comp2']">   <component>...</component> <keep-alive> <!-- exclude --> <keep-alive v-bind:exclude="['comp3','comp4']">   <component>...</component> <keep-alive>  | 
					
正規表現
| 
					 1 2 3 4 5 6 7 8 9  | 
						<!-- include --> <keep-alive v-bind:include="/comp[12]/">   <component>...</component> <keep-alive> <!-- exclude --> <keep-alive v-bind:exclude="/comp[34]/">   <component>...</component> <keep-alive>  | 
					
上記の例では、include属性で「comp1」と「comp2」をキャッシュ対象に指定し、exclude属性で「comp3」と「comp4」をキャッシュ非対象に指定しています。
状態を維持すべきキャッシュ対象のコンポーネントが限定されている場合は、max属性ではなくinclude属性、またはexclude属性を使用する方が、より効率的にキャッシュを行うことができます。