Vue.js のフォームバリデーションライブラリ VeeValidate を評価してみた
前職で Vue.js を用いた開発業務を行っていました。
クラスメソッドに入社後も Vue.js を使った開発業務を担当することになったのですが、フォームバリデーション処理が本質的ではない割に毎回発生することに負担を感じていたので、その辺りを肩代わりしてくれるライブラリを探すことにしました。
その結果 VeeValidate というライブラリが割と引っ掛かり、レビューブログやドキュメントを眺めたりした感じ良さげだったので、評価してみました。
環境情報
- Node.js: 12.18.2
- Vue.js: 2.6.11
- Vuetify: 2.3.3
- VeeValidate: 3.3.7
- Vue CLI: 4.4.6
Vuetify 組み込みバリデーションを使用する場合
以前よりコンポーネントライブラリ Vuetify と組み合わせて使用しています。
Vuetify は組み込みのバリデーション機構を持っています。
こちらはAWSアカウントIDとバケット名を検査するフォームの例です (本当はもっと細かいバリデーションが必要ですが、あくまでも簡易的なものということで…)。
https://github.com/teknocat/veevalidate-test/blob/master/src/components/VuetifyForm.vue
<template> <v-form ref="form" v-model="valid" > <v-text-field v-model="awsAccountId" :rules="[rules.required, rules.awsAccountId]" label="AWSアカウントID" required ></v-text-field> <v-text-field v-model="bucketName" :rules="[rules.required, rules.bucketName]" label="バケット名" required ></v-text-field> <v-btn :disabled="!valid" color="success" class="mr-4" @click="submit" > Submit </v-btn> </v-form> </template> <script> export default { name: 'VuetifyForm', data: () => ({ valid: true, awsAccountId: '', bucketName: '', rules: { required: (value) => !!value || '必須項目です', awsAccountId: (value) => { return /^[0-9]{12}$/.test(value) || 'AWSアカウントID は12桁の数値です'; }, bucketName: (value) => { return /^[a-z0-9.-]{3,63}$/.test(value) || 'バケット名 のフォーマットが正しくありません'; }, }, }), methods: { submit () { const result = this.$refs.form.validate(); console.log('submit', result); }, }, } </script>
バリデーション動作はするのですが、以下のようなつらみがありました。
- ルールやメッセージ処理を各コンポーネントの中で実装するため、他コンポーネント間での流用がしにくい
- 共通化を独自で設計から考えるのは辛いし、フォーム内の各コントローラへの参照の嵐になりそう(私は諦めた)
- 複数コントロール間の依存関係をもったバリデーションが難しい
- 各コントロールのイベントや値を見張ってチェックする必要があり、とても辛い(バグも出やすい)
- ルール評価処理中に、どのコントロールを対象にしているかの情報が取得出来ない?
- コード上
rules.required
のメッセージ「必須項目です
」に、本当は各フィールド名(AWSアカウントID
、バケット名
)を入れたい
- コード上
- メッセージのI18n化を考えると、より複雑になる
VeeValidate を使用する場合
VeeValidate でほぼ同等のバリデーションを実装すると、以下のような感じになります。
https://github.com/teknocat/veevalidate-test/blob/master/src/components/VeeValidateForm.vue
<template> <ValidationObserver ref="observer" v-slot="{ invalid }" immediate> <form> <ValidationProvider v-slot="{ errors, valid }" name="AWSアカウントID" rules="required|awsAccountId"> <v-text-field v-model="awsAccountId" :error-messages="errors" label="AWSアカウントID" required :success="valid" ></v-text-field> </ValidationProvider> <ValidationProvider v-slot="{ errors, valid }" name="バケット名" rules="required|bucketName"> <v-text-field v-model="bucketName" :error-messages="errors" label="バケット名" required :success="valid" ></v-text-field> </ValidationProvider> <v-btn class="mr-4" @click="submit" :disabled="invalid" color="success">submit</v-btn> </form> </ValidationObserver> </template> <script> import { required } from 'vee-validate/dist/rules'; import { localize, extend, ValidationObserver, ValidationProvider } from 'vee-validate'; import ja from 'vee-validate/dist/locale/ja.json'; extend('required', required); extend('awsAccountId', (value) => { return /^[0-9]{12}$/.test(value) || '{_field_} は12桁の数値です'; }); extend('bucketName', (value) => { return /^[a-z0-9.-]{3,63}$/.test(value) || '{_field_} のフォーマットが正しくありません'; }); localize('ja', ja); export default { components: { ValidationProvider, ValidationObserver, }, data: () => ({ awsAccountId: '', bucketName: '', }), methods: { submit () { this.$refs.observer.validate().then(result => { console.log('submit', result); }); }, }, } </script>
ValidationObserver
, ValidationProvider
で従来のフォーム、コントロールを囲むような感じになり、一見テンプレートが複雑になったような感じです。
一方、ルールやメッセージ定義は extend
を使うことでコンポーネントから独立しているため、定義を一箇所にまとめることが容易になっています。
例は SFC に extend
処理も入れていますが、例えば定義したファイルを main.js
から呼ぶことでグローバルな設定にも出来ます。
また、VeeValidate にはよく使うルール+メッセージが組み込まれており、この例では required
を使用しています。ソース上では以下の行のみ書いており、必須チェックの実装やメッセージ定義が無いことがわかります(日本語の組み込み済メッセージを表示するために locale
の設定が別途必要)。
import { required } from 'vee-validate/dist/rules'; extend('required', required);
一例として、以下のようなルールがあらかじめ組み込まれています。 バリデーションを実装していた人間にとっては、かゆい所に手が届くようなラインナップです。
alpha_num
数字とアルファベットのみ- 全角は対象外
between
数値の範囲confirmed
確認用フィールドemail
required_if
あるフィールドが特定の値の場合のみ別フィールドを必須にする- アップロードするファイルの拡張子や MIME type チェック、ファイルサイズチェックなど
さらに、Vuetify 組み込みの課題に上げていた、メッセージに対象フィールド名を入れられない問題についても、{_field_}
と書くだけで実際のフィールド名に置換してくれます(I18nへの対応も確認済)。
VeeValidate 評価ポイント
Overview に利点が上げられていますが、個人的には以下のポイントを評価しました。
- ルール、メッセージ定義の共通化が容易(先に述べたとおり)
- よくあるルールはあらかじめ組み込まれている(先に述べたとおり)
- 複数のフィールドに対するバリデーションに対応
- フォーム、コントロールに対する様々なステータスを管理
- Validation State | VeeValidate
dirty
とか嬉しい。まともに実装すると面倒なやつ
- サーバ側でバリデーションしてエラーメッセージを返す方式にも対応
また、Vue 3.0 に対する言及を含め、本人が降臨している reddit の投稿が決め手となりました。
最後に
VeeValidate は取っ掛かり複雑な仕組みに見えるものの、開発が進むにつれ、コンポーネントの増加、バリデーション要件の複雑化により得られる恩恵は大きくなりそう、という印象です。
メッセージ以外も含めた統一的な I18n 化(Vue I18nとの連携)の仕組みを実現するにはひと工夫必要で、現時点で一応動作しているものの、完全にこれでイケる、というところまでは至っていません。追加できるような情報があれば、別途記載したいと思います。
なお、本サンプルは以下のリポジトリに置いています。