【Vue.js】ミックスインでコンポーネントオプションを追加する

# ミックスインとは
- オプションを他のコンポーネントに混ぜ込む(マージする)機能
- 複数のコンポーネントで共通の処理を実行したい場合に使える
- ミックスインオブジェクトとコンポーネントの定義がコンフリクトした場合は、コンポーネントのデータが優先される
- フック(createdなど)は、ミックスインに定義されているものから先に呼び出される
- 1つのコンポーネントから複数のミックスインオブジェクトを呼び出すことも可能
- 複数のミックスインオブジェクト同士で定義がコンフリクトしている場合は、後で呼び出されているオブジェクトの定義が優先される
- 上記の場合、createフックは先に呼び出されているミックスインオブジェクトから実行される

# 補足
## extendとdataオプション
extendを使用する場合、extendしたいオブジェクトのdataオプションの記述方法が少し異なります。

この記述方法だとエラーが発生します。

```vue_mixin_test.html
<script>
data: {
    foo: 'abc',
    message: 'hello'
},
</script>
```

```
[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.
```

メッセージの通り、dataオプションをfunctionで返せば問題ありません。
以下の記述方法はextendを使用しない場合も使えるので、こちらの方が無難かもしれません。

```vue_mixin_test.html
<script>
data: function () {
    return {
        foo: 'abc',
        message: 'hello'
    }
},
</script>
```

公式サイトにも説明があります。
>[コンポーネントオプション〜データ](https://012-jp.vuejs.org/api/options.html#data)

## ライフサイクルフック
createdフックなど、インスタンス生成時に初期化として実行される関数のことです。

ミックスインオブジェクトに定義されているフック関数は、コンポーネントのフック関数より先に呼び出されます。
ミックスインオブジェクトはこの時点で既にマージされているので、定義がコンポーネントとコンフリクトしている場合は、コンポーネントのデータが呼び出されます。
>[Vue インスタンス〜インスタンスライフサイクルフック](https://jp.vuejs.org/v2/guide/instance.html#%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%95%E3%82%B5%E3%82%A4%E3%82%AF%E3%83%AB%E3%83%95%E3%83%83%E3%82%AF)

フック関数は複数あり、それぞれ実行されるタイミングが異なります。
>[Vue インスタンス〜ライフサイクルダイアグラム](https://jp.vuejs.org/v2/guide/instance.html#%E3%83%A9%E3%82%A4%E3%83%95%E3%82%B5%E3%82%A4%E3%82%AF%E3%83%AB%E3%83%80%E3%82%A4%E3%82%A2%E3%82%B0%E3%83%A9%E3%83%A0)

# ソースコード
[公式サイト](https://jp.vuejs.org/index.html)からお借りしました。

```vue_mixin_test.html
<!DOCTYPE>
<head>
  <meta charset="UTF-8">
  <title>Vue.js_mixin_test</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
  <script>
      var mixin = {
          data: function () {
              return {
                  hoge: 'hoge',
                  foo: 'abc',
                  // 重複するプロパティ
                  message: 'hello'
              }
          },
          methods: {
              fooMethod: function () {
                  console.log('foo')
              },
              // 重複するメソッド
              conflicting: function () {
                  console.log('from mixin')
              }
          },
          // this.$dataはmixinがマージされた後の状態で表示される
          created: function () {
              console.log('mixin', this.$data)
              this.fooMethod()
              this.conflicting()
          }
      }

      var mixin2 = {
          data: function () {
              return {
                  foo: 'abc2',
                  message: 'hello2'
              }
          },
          methods: {
              fooMethod: function () {
                  console.log('foo2')
              },
              conflicting: function () {
                  console.log('from mixin2')
              }
          },
          created: function () {
              console.log('mixin2', this.$data)
              this.fooMethod()
              this.conflicting()
          }
      }

      // 複数のmixinを呼び出すことも可能
      var Component = Vue.extend({
          mixins: [mixin, mixin2]
      })

      new Component()

      var vm = new Vue({
          // mixinをマージする
          mixins: [mixin],
          // mixinと定義が重複している場合、コンポーネントのデータが優先される
          data: function () {
              return {
                  bar: 'def',
                  // 重複するプロパティ
                  message: 'goodbye'
              }
          },
          methods: {
              barMethod: function () {
                  console.log('bar')
              },
              // 重複するメソッド
              conflicting: function () {
                  console.log('from self')
              }
          },
          created: function () {
              console.log('self', this.$data)
              this.barMethod()
              this.conflicting()
          }
      })

      vm.fooMethod()
      vm.barMethod()
      vm.conflicting()
  </script>
</body>
```