On Vue Events

これは記事の日本語版です。

The English version of this article is here.

Vueでは、コンポーネントは主に2つの仕組みで情報を受けます。「prop」と「event」です。その中、1つの方が優れています。

Vueのpropとは、コンポーネントの汎用的な引数のことです。Propを通して、コンポーネントは自分の環境の情報をテスしやすい仕組みで受付することができます。例えば、下記の「タイトルコンポーネント」は表示する内容をpropで受けています:

<template>
  <div>
    {{ title }}
  </div>
</template>
export default Vue.extend({
  props: {
    title: {
      type: String,
    },
  },
})

Propは実行時に型がチェックされるので、開発する際にバグの警告がよくあります。そして、TypeScriptを活かせば、静的にもチェックすことが可能です。Veturの「interpolation feature」はその1つです。

たまに、コンポーネントはユーザの操作に反応し、親に処理を行う機会を付与します。コールバック関数をpropとして渡せば、親コンポーネントが自由にその処理が制御できるようになります。例えば、次はそういう機能を持っているボタンコンポーネント:

<template>
  <button @click="click">
    Click me!
  </button>
</template>
export default Vue.extend({
  props: {
    click: {
      type: Function as PropType<() => Promise<void>>,
    },
  },
})

コールバック関数の戻り値をPromiseにすれば、コンポーネントは「処理中」「ロージング」状態のUIが表示できるようになります。更に、Promiseが失敗する可能性もあるので、このコンポーネントに即したエラー表示を見せるチャンスもできています。

素晴らしいです。柔軟です。これ以上は不要です。

うん、本当に他には何も要りません。でも、なぜか、他のものは確かに存在します。Eventです。

Eventとは、コールバックpropと似ていますが、コールバックpropの利益は一切ありません。下記のeventで実装されたボタンコンポーネントを見ましょう。

<template>
  <button @click="click">
    Click me!
  </button>
</template>
export default Vue.extend({
  methods: {
    click() {
      this.$emit('click')
    }
  }
})

$emitという魔法的な関数がイベントを発行しています。残念ですが戻り値はvoidで、エラーも作れません。「処理中」状態が作れないばかりか、成功かどうかですら分かりません。ただの関数の呼び出しに見えるせいで、静的に型を分析するツールはイベント名も引数も中々拾えません。

全部コールバックpropにすべきでした。Vueの方針が間違っています。

Eventを使うのを止めましょう。