基礎からメモ: Vue.js CH5-p184 その他の機能やオプション

関数型コンポーネント

functionalオプションを付けることで、関数型コンポーネント(状態とインスタンスを持たない)を定義できる。

Vue.component('functional-component', {
  functional: true,
  render: function(createElement, context) {
    return createElement('div', context.props.message);
  },
  props: {
    message: String
  }
});

プリコンパイルを行わない場合は、文字列テンプレートではなく描画関数を使う必要がある。

単一ファイルコンポーネントの場合は、コンパイルされるので描画関数は要らない次の書き方ができる。

<template functional>
  <p>単一ファイルコンポーネントなら描画関数は要らない</p>
</template>

動的なコンポーネント

特殊な属性isコンポーネントを指定することで複数のコンポーネントを切り替えることができる。

2種類のコンポーネントを定義

Vue.component('my-component-a', {
  template: '<div class="my-component-a">component A</div>'
});

Vue.component('my-component-b', {
  template: '<div class="my-component-b">component B</div>'
});

メインのコンストラク

var app = new Vue({
  el: '#app',
  data: {
    componentTypes: ['my-component-a', 'my-component-b'],
    current: 0
  },
  computed: {
    component: function() {
      return this.componentTypes[this.current]
    }
  }
});

コンポーネント

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

current^=1でビット排他的論理和代入演算子を使って、ボタンを押すたびにcurrentに1と0を交互に代入している。

Mixin

コンストラクタのオプションを抜き出して、コンポーネントに混ぜ込む機能。複数のコンポーネントで同一処理をする場合、Mixinを使って共通化できる。

let mixin = {
  created:function() {
    this.hello()
  },
  methods: {
    hello: function() {
      console.log('hello from mixin!')
    }
  }
}

Vue.component('my-component-a', {
  mixins: [mixin],
  template: '<div class="my-component-a">component A</div>'
});

Vue.component('my-component-b', {
  mixins: [mixin],
  template: '<div class="my-component-b">component B</div>'
});

keep-aliveで状態を維持する

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

コンポーネントインスタンスにはそれぞれのライフサイクルがある。

v-ifディレクティブでコンポーネントの描画状態が変わったり、動的コンポーネントで要素が切り替わったりすると、コンポーネントインスタンスは破棄されライフサイクルが初期化される。先程の動的コンポーネントMixinを使った例では、要素が切り替わるたびにMixinされた要素のcreatedに登録された関数からメソッドが呼び出されるが、createdライフサイクルフックはインスタンスの作成時にリアクティブ関係の初期化が終わったタイミングで呼ばれるので、切り替わるたびにライフサイクルが初期化されているのがわかる。

v-ifの記述場所による違い

コンポーネントのカスタムタグに書いたv-if

showプロパティがfalseになるとコンポーネントは破棄される。

<comp-child v-if="show"></comp-child>
コンポーネントのルートタグに書かれたv-if

コンポーネント内で自分自身のテンプレートのルートタグにv-ifがある場合、showプロパティがfalseになってもコンポーネントは破棄されない。 もしその内側に子コンポーネントがある場合、その子コンポーネントは破棄される。

<div class="child" v-if="show">
コンポーネントのテンプレート
</div>

keep-aliveで状態維持

次の動的コンポーネントを使った例では、「メッセージ一覧」と「投稿フォーム」をボタンで切り替えるたびに、フォームに書き込んだ内容は消えてしまう。

<div id="app">
  <button v-on:click="current='comp-board'">メッセージ一覧</button>
  <button v-on:click="current='comp-form'">投稿フォーム</button>
  <div v-bind:is="current"></div>
</div>
Vue.component('comp-board', {
  template: '<div class="board">Message Board</div>'
})

Vue.component('comp-form', {
  template: '<div class="form">Form<textarea v-model="message"></textarea></div>',
  data: function() {
    return { message: '' }
  }
})

var app = new Vue({
  el: '#app',
  data: {
    current: 'comp-board'
  },
});

この切り替わる部分に対して、要素をkeep-aliveタグで囲むと、フォームに入力した内容が保持されるようになる。

<keep-alive>
  <div v-bind:is="current"></div>
</keep-alive>

keep-aliveが適用されたコンポーネントは、親コンポーネントが破棄されたときは保持された状態も一緒に破棄される。key属性が設定されている場合はkeyごとの状態を保持する。

keep-aliveで追加されるライフサイクルフック

keep-aliveが適用されたコンポーネントでは、活性化されたか非活性化されたかを判別するために次のライフサイクルフックが使える。

メソッド名 タイミング
activated <keep-alive>を適用したコンポーネントが活性化したとき
deactivated <keep-alive>を適用したコンポーネントが非活性化したとき