Vueのカスタムイベントで .prevent する

2021/08/20に公開

要約

Vue テンプレートのイベントハンドラーにおける .prevent modifier は $event.preventDefault() と等価なので、カスタムイベントであっても preventDefault メソッドさえ定義すれば使うことができる。

おわり

.prevent modifier とは

Vue テンプレートで a 要素などに対して @click.prevent="..." のようにしてイベントハンドラーを書くと、その要素の click イベントにおけるデフォルトのアクションを抑制することができる。

  • <a href="http://example.com/">hoge</a>
    • リンクをクリックすると http://example.com/ へのナビゲーションが行われる
  • <a href="http://example.com/" @click.prevent>hoge</a>
    • クリックしても何も起きない

つまりは Event.preventDefault() を呼ぶのと同じ処理を簡潔に書くことができるのが .prevent modifier である。

カスタムイベントではどうなるか

カスタムイベントでも preventDefault メソッドを定義することで .prevent modifier を使うことができる。

App.vue
<!-- 呼ぶ側 -->
<template>
  <Child @hoge.prevent />
</template>

<script>
  import Child from './Child.vue'

  export default {
    name: 'App',
    components: {
      Child
    }
  }
</script>
Child.vue
<!-- 呼ばれる側 -->
<template>
  <button @click="handleClick">hoge</button>
</template>

<script>
  export default {
    name: 'Child',
    methods: {
      handleClick() {
        const evt = {
          preventDefault() {
            console.log('prevent')
          }
        }
        this.$emit('hoge', evt)
      }
    }
  }
</script>

この例の場合、Child コンポーネント内にあるボタンをクリックするとブラウザのコンソールには prevent の文字列が出力される。

Child コンポーネントでは this.$emit('hoge', evt) で渡す evt に preventDefault メソッドを実装しているため、カスタムイベントであっても <Child @hoge.prevent /> としたときに独自に定義した preventDefault の処理を実行させることができるという訳である。

コンパイルされた結果を確認する

もう少し深掘りして、テンプレート内の @hoge.prevent がどうコンパイルされているのか確認してみる。

vue-cli で vue serve App.vue を実行してブラウザのデバッガで確認すると、App.vue のテンプレートの部分は以下のようなソースコードとして出力されていることが分かる。

var render = function() {
  var _vm = this
  var _h = _vm.$createElement
  var _c = _vm._self._c || _h
  return _c("Child", {
    on: {
      hoge: function($event) {
        $event.preventDefault()
      }
    }
  })
}
var staticRenderFns = []
render._withStripped = true

export { render, staticRenderFns }

少し見やすく手直しするとこのような感じになる。
(render 関数の詳細については公式ドキュメントの Render Functions & JSX を参照)

export default {
  render(h) {
    // <Child @hoge.prevent />
    return h("Child", {
      on: {
        hoge: function($event) {
	  $event.preventDefault()
	}
      }
    })
  }
}

つまり、Vue テンプレートにおいて <Child @hoge.prevent /><Child @hoge="$event.preventDefault()" /> と書くことと同じであることが分かる。

また、Vue.js の src/compiler/codegen/events.js を見ると .prevent 以外の modifier についてもどのような変換が行われているかが分かる。

おわり

これといってカスタムイベントで .prevent が使いたかったわけではないものの、間違えて .prevent を書いた時になんか TypeError: $event.preventDefault is not a function というエラーが出てきたので気になって調べたメモでした。これが分かっても特に使う用事がない。

Discussion