<div id="app" class="p-4">
  <validation-observer v-slot="{ handleSubmit }" ref="observer">
    <form @submit.prevent="handleSubmit(submitForm)" class="form">
      <div class="mb-3">
        <label for="name" class="col-form-label">姓名</label>
        <validation-provider rules="required" name="姓名" v-slot="{ errors, classes }">
          <input type="text" class="form-control" v-model="name" :class="classes" />
          <small class="invalid-feedback">{{ errors[0] }}</small>
        </validation-provider>
      </div>
      <div class="mb-3">
        <label for="email" class="col-form-label">E-mail</label>
        <validation-provider rules="required|email" name="E-mail" v-slot="{ errors, classes }">
          <input type="email" class="form-control" v-model="email" :class="classes" />
          <small class="invalid-feedback">{{ errors[0] }}</small>
        </validation-provider>
      </div>
      <button type="submit" class="btn btn-primary">送出表單</button>
    </form>
  </validation-observer>
</div>
.form {
  max-width: 400px;
}
.invalid-feedback {
  font-size: 0.75rem;
}
View Compiled
Vue.use(VeeValidate);
const TW = {
  code: "zh_TW",
  messages: {
    alpha: "{_field_} 須以英文組成",
    alpha_dash: "{_field_} 須以英數、斜線及底線組成",
    alpha_num: "{_field_} 須以英數組成",
    alpha_spaces: "{_field_} 須以英文及空格組成",
    between: "{_field_} 須介於 {min} 至 {max}之間",
    confirmed: " {_field_} 不一致",
    digits: "{_field_} 須為 {length} 位數字",
    dimensions: "{_field_} 圖片尺寸不正確。須為 {width} x {height} 像素",
    email: "{_field_} 須為有效的電子信箱",
    excluded: "{_field_} 的選項無效",
    ext: "{_field_} 須為有效的檔案",
    image: "{_field_} 須為圖片",
    oneOf: "{_field_} 的選項無效",
    integer: "{_field_} 須為整數",
    length: "{_field_} 的長度須為 {length}",
    max: "{_field_} 不能大於 {length} 個字元",
    max_value: "{_field_} 不得大於 {max}",
    mimes: "{_field_} 須為有效的檔案類型",
    min: "{_field_} 不能小於 {length} 個字元",
    min_value: "{_field_} 不得小於 {min}",
    numeric: "{_field_} 須為數字",
    regex: "{_field_} 的格式錯誤",
    required: "{_field_} 為必填",
    required_if: "{_field_} 為必填",
    size: "{_field_} 的檔案須小於 {size}KB",
    double: "{_field_}字段必須為有效的小數"
  }
};
import * as rules from 'https://cdn.jsdelivr.net/npm/vee-validate@3.4.12/dist/rules.js';

Object.keys(rules).forEach(rule => {
  VeeValidate.extend(rule, rules[rule]);
});

VeeValidate.localize('zh_TW', TW);
                     
VeeValidate.configure({
  mode: 'eager', // Interaction Modes
  classes: {
    valid: 'is-valid',
    invalid: 'is-invalid'
  }
});

Vue.component('ValidationObserver', VeeValidate.ValidationObserver);
Vue.component('ValidationProvider', VeeValidate.ValidationProvider);

new Vue({
  el: "#app",
  data() {
    return {
      name: '',
      email: ''
    }
  },
  methods: {
    submitForm() {
      console.log('submit');
    }
  }
});

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.0.2/css/bootstrap.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.8/vue.min.js
  2. https://cdn.jsdelivr.net/npm/vee-validate@3.4.12/dist/vee-validate.js
  3. https://cdn.jsdelivr.net/npm/vee-validate@3.4.12/dist/locale/zh_TW.json