[Vue] 引用外部套件
TL;DR
在載入套件的時候,可以使用npm或是cdn的方式引入。
載入後,則是可以透過app.use或是元件的方式去啟用。
參考資料
- 如何為單一表單(input) 進行驗證 | 六角學院HackMD
相關連結
- vue-axios (app.use範例)
- VeeValidate | 元件範例
- Vite | Official
- create-vue | Github
- random user | 測試用假資料API
CDN載入VeeValidate
官方簡單範例
- html(#app)
- vue
- css
<div id="app">
  <!-- 送出表單時,v-form會自動驗證規則是否符合 -->
  <v-form @submit="onSubmit">
    <!-- name內的名稱可以自訂,會對應錯誤訊息的name (元件需要加上頭尾標籤)-->
    <v-field name="name" type="text" placeholder="Who are you" :rules="isRequired"></v-field>
    <!-- name對應v-field上的name -->
    <error-message name="name"></error-message>
    <button>Submit</button>
  </v-form>
</div>
const App = {
  components: {
    // Components were renamed to avoid conflicts of HTML form element without a vue compiler
    // 因為使用CDN載入,預設會將套件賦予到VeeValidate這個變數上,我們要將VeeValidate物件下的各個元件給取出
    // 為了避免與HTML標籤搞混,這邊加上V前綴做為辨識
    VForm: VeeValidate.Form,
    VField: VeeValidate.Field,
    ErrorMessage: VeeValidate.ErrorMessage,
  },
  methods: {
    isRequired(value) {
      if (!value) {
        return 'this field is required';
      }
      
      return true;
    },
    onSubmit(values) {
      alert(JSON.stringify(values, null ,2));
    }
  }
}
Vue.createApp(App).mount('#app')
span {
  display: block;
  margin: 10px 0;
}
button, input {
  display: block;
}
因為這邊在Codepen上使用CDN的方式去載入套件,會將VeeValidate賦予到同名稱的變數上。
所以在註冊元件時,需使用VeeValidate.Form,VeeValidate.Field,VeeValidate.ErrorMessage去分別取出三個元件。
並且為了避免元件名稱與html標籤名稱相同,另外賦予一個加上V前綴的名稱( Line:6 ~ Line:8 (Vue))
<v-form @submit="onSubmit" v-slot="all">
  {{all}}
  <v-field name="name" type="text" placeholder="Who are you" :rules="isRequired">
  </v-field>
  <error-message name="name"></error-message>
  <button>Submit</button>
</v-form>
更多範例
- html(#app)
- vue
<div id="app">
  <div class="p-5">
    <h3>範例:載入 VeeValidate 驗證套件</h3>
    <v-form @submit="onSubmit" v-slot="{errors}">
      {{errors}}
      <div class="mb-3">
        <label for="email" class="form-label">Email</label>
        <!-- 使用veevalidate提供的規則,不需動態綁定,使用rule="..."即可,需要多個規則可以使用|隔開(不能加上空白) -->
        <v-field id="email" name="email" type="email" class="form-control"
                 :class="{ 'is-invalid': errors['email'] }"
                 rules="email|required"
                 placeholder="請輸入 Email">
        </v-field>
        <error-message name="email" class="invalid-feedback"></error-message>
      </div>
      <!-- 自行練習 -->
      <div class="mb-3">
        <label for="name" class="form-label">姓名</label>
        <v-field id="name" name="姓名" type="text" class="form-control"
                 placeholder="請輸入姓名"></v-field>
        <span class="invalid-feedback"></span>
      </div>
      <div class="mb-3">
        <label for="phone" class="form-label">電話</label>
        <!-- 使用自己定義的methods時,需要用動態綁定:rules -->
        <!-- name可以設定中文,會與errors的屬性名稱相同,所以透過error['電話']的有無可以切換is-invalid的class -->
        <v-field id="phone" name="電話" type="text" class="form-control"
                 :rules="isPhone"
                 :class="{ 'is-invalid': errors['電話'] }"
                 placeholder="請輸入電話"
                 v-model="user.phone"
                 >
        </v-field>
        <error-message class="invalid-feedback" name="電話"></error-message>
      </div>
      <!-- 自行練習 -->
      <div class="mb-3">
        <label for="region" class="form-label">地區</label>
        <select id="region" name="地區" class="form-control">
          <option value="">請選擇地區</option>
          <option value="台北市">台北市</option>
          <option value="高雄市">高雄市</option>
        </select>
        <span class="invalid-feedback"></span>
      </div>
      <!-- 自行練習 -->
      <div class="mb-3">
        <label for="address" class="form-label">地址</label>
        <input id="address" name="地址" type="text" class="form-control"
               placeholder="請輸入地址">
        <span class="invalid-feedback"></span>
      </div>
      <button class="btn btn-primary" type="submit">Submit</button>
    </v-form>
  </div>
</div>
VeeValidate.defineRule('email', VeeValidateRules['email']);
VeeValidate.defineRule('required', VeeValidateRules['required']);
VeeValidateI18n.loadLocaleFromURL('./zh_TW.json');
VeeValidate.configure({
  generateMessage: VeeValidateI18n.localize('zh_TW'),
  validateOnInput: true, // 調整為:輸入文字時,就立即進行驗證
});
const app = Vue.createApp({
  data() {
    return {
      user: {
        email: '',
        name: '',
        address: '',
        phone: ''
      }
    }
  },
  methods: {
    onSubmit(values) {
      alert(JSON.stringify(values, null, 2));
    },
    isPhone(value) {
      const phoneNumber = /^(09)[0-9]{8}$/
      return phoneNumber.test(value) ? true : '需要正確的電話號碼'
    }
  },
  created() {
    console.log(this);
  }
});
app.component('VForm', VeeValidate.Form);
app.component('VField', VeeValidate.Field);
app.component('ErrorMessage', VeeValidate.ErrorMessage);
app.mount('#app');  
可以直接在v-field上綁定v-model,會直接修改data內的狀態。
另外也可以如同官方提供的範例,直接在觸發@submit事件時,自訂義要送出時的行為 透過傳入一個values參數,可以將各個參數的值加工後(if needed)傳送到後端。
為單一表單進行驗證(VeeValidate)
VeeValiadation 分為 Cli 與 CDN 版本,本範例以 CDN 為主,觀念上兩者並無太大差異。
文件:
CDN:
- 主套件:https://cdnjs.cloudflare.com/ajax/libs/vee-validate/4.5.8/vee-validate.min.js
- Rules:https://cdn.jsdelivr.net/npm/@vee-validate/rules@4.5.8/dist/vee-validate-rules.min.js
- i18n:https://cdn.jsdelivr.net/npm/@vee-validate/i18n@4.5.8/dist/vee-validate-i18n.min.js
- 
載入外部CDN <script src="https://cdnjs.cloudflare.com/ajax/libs/vee-validate/4.5.8/vee-validate.min.js"></script>
 <script src="https://cdn.jsdelivr.net/npm/@vee-validate/rules@4.5.8/dist/vee-validate-rules.min.js"></script>
 <script src="https://cdn.jsdelivr.net/npm/@vee-validate/i18n@4.5.8/dist/vee-validate-i18n.min.js"></script>
- 
註冊元件 註冊全域的表單驗證元件(VForm, VField, ErrorMessage) const app = Vue.createApp({
 // ...
 });
 app.component('VForm', VeeValidate.Form);
 app.component('VField', VeeValidate.Field);
 app.component('ErrorMessage', VeeValidate.ErrorMessage);
 app.mount('#app');
- 
定義規則 選擇加入特定規則,全規則可參考連結:官方@vee-validate/rules VeeValidate.defineRule('email', VeeValidateRules['email']);
 VeeValidate.defineRule('required', VeeValidateRules['required']);全部加入 Object.keys(VeeValidateRules).forEach(rule => {
 VeeValidate.defineRule(rule, VeeValidateRules[rule]);
 });全部加入(CDN 版本) Object.keys(VeeValidateRules).forEach(rule => {
 if (rule !== 'default') {
 VeeValidate.defineRule(rule, VeeValidateRules[rule]);
 }
 });
- 
加入多國語系 將外部資源儲存到本地,方便稍後使用。 // 讀取剛剛存取的外部的資源
 VeeValidateI18n.loadLocaleFromURL('./zh_TW.json');
 // Activate the locale
 VeeValidate.configure({
 generateMessage: VeeValidateI18n.localize('zh_TW'),
 validateOnInput: true, // 調整為:輸入文字時,就立即進行驗證
 });
- 
套用 v-form並加入v-slot<v-form v-slot="{ errors }" @submit="onSubmit" >備註:v-slot 稱為插槽 Props,可以將驗證結果的回饋資料直接帶入於區塊中 
- 
套用 v-field及error-message常用技巧: - name為必填,是錯誤驗證的回饋欄位,會與多個項目進行匹配(errors, errors-message …)
- 為 v-field帶入全域設定的規則,可參考相關文件
- as可以改變- v-field的型態,如以下- select範例
- :class可運用- v-form帶入的驗證錯誤作為判斷
 <v-field
 id="email"
 name="email"
 type="email"
 class="form-control"
 :class="{ 'is-invalid': errors['email'] }"
 placeholder="請輸入 Email" rules="email|required"
 v-model="user.email"
 ></v-field>
 <error-message name="email" class="invalid-feedback"></error-message>select範例<v-field
 id="name"
 name="地區"
 class="form-control"
 :class="{ 'is-invalid': errors['地區'] }"
 placeholder="請輸入地區"
 rules="required"
 v-model="user.region"
 as="select"
 >
 <option value="">請選擇地區</option>
 <option value="台北市">台北市</option>
 <option value="高雄市">高雄市</option>
 </v-field>
- 
加入自訂驗證、送出表單等行為 範例:自訂驗證 rules中可自訂函式來驗證結果
 使用v-bind綁定:rules="isPhone"<div class="mb-3">
 <label for="phone" class="form-label">電話</label>
 <v-field
 id="phone"
 name="電話"
 type="text"
 class="form-control"
 :class="{ 'is-invalid': errors['電話'] }"
 placeholder="請輸入電話"
 :rules="isPhone"
 v-model="user.phone"
 ></v-field>
 <error-message name="電話" class="invalid-feedback"></error-message>
 </div>methods: {
 isPhone(value) {
 const phoneNumber = /^(09)[0-9]{8}$/
 return phoneNumber.test(value) ? true : '需要正確的電話號碼'
 }
 }範例:送出表單 <v-form v-slot="{ errors }" @submit="onSubmit" >
 ...
 <button class="btn btn-primary" type="submit">Submit</button>
 </v-form>methods: {
 onSubmit() {
 // ...
 },
 },
VueCLI NPM載入VeeValidate
- main.js
- AboutView.vue
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// 匯入 vee-validate 主套件
import { Field, Form, ErrorMessage, defineRule, configure } from 'vee-validate'
// 匯入 vee-validate 相關規則
import { required, email, min } from '@vee-validate/rules'
// 匯入多國語系的功能
import { localize, setLocale } from '@vee-validate/i18n'
// 匯入繁體中文語系檔案
import zhTW from '@vee-validate/i18n/dist/locale/zh_TW.json'
// 定義驗證規則
defineRule('required', required)
defineRule('email', email)
defineRule('min', min)
// 設定 vee-validate 全域規則
configure({
  generateMessage: localize({ zh_TW: zhTW }), // 載入繁體中文語系
  validateOnInput: true // 當輸入任何內容直接進行驗證
})
// 設定預設語系
setLocale('zh_TW')
const app = createApp(App).use(router)
// 註冊 vee-validate 三個全域元件
// eslint-disable-next-line vue/multi-word-component-names
app.component('Form', Form)
// eslint-disable-next-line vue/multi-word-component-names
app.component('Field', Field)
app.component('ErrorMessage', ErrorMessage)
app.mount('#app')
<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
  <CardTest></CardTest>
  <Form v-slot="{ errors, values, validate }" @submit="onSubmit">
    {{ errors }} {{ values }}
    <div class="mb-3">
      <label for="email" class="form-label">Email</label>
      <Field id="email" name="email" type="email" class="form-control" :class="{ 'is-invalid': errors['email'] }"
        placeholder="請輸入 Email" rules="email|required" v-model="user.email"></Field>
      <error-message name="email" class="invalid-feedback"></error-message>
    </div>
    <button class="btn me-2 btn-outline-primary" type="button" @click="validate">驗證</button>
    <button class="btn btn-primary" type="submit">Submit</button>
  </Form>
</template>
<script>
import CardTest from '@/components/CardTest.vue'
export default {
  components: {
    CardTest
  },
  data () {
    return {
      user: {}
    }
  },
  methods: {
    onSubmit () {
      console.log(this.user)
    }
  },
  created () {
    console.log(this)
  }
}
</script>
Vite載入vue-axios
安裝vue-axios
npm install --save axios vue-axios
package.json內容
package.json內容{
"name": "vite-project",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
  "dev": "vite",
  "build": "vite build",
  "preview": "vite preview",
  "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
  "format": "prettier --write src/"
},
"dependencies": {
  "axios": "^1.6.2",
  "pinia": "^2.1.7",
  "vue": "^3.3.10",
  "vue-axios": "^3.5.2",
  "vue-router": "^4.2.5"
},
"devDependencies": {
  "@rushstack/eslint-patch": "^1.3.3",
  "@vitejs/plugin-vue": "^4.5.1",
  "@vue/eslint-config-prettier": "^8.0.0",
  "eslint": "^8.49.0",
  "eslint-plugin-vue": "^9.17.0",
  "prettier": "^3.0.3",
  "vite": "^5.0.5"
}
}
調整main.js
import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import axios from 'axios'
import VueAxios from 'vue-axios'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.use(VueAxios, axios)
app.mount('#app')
測試是否載入成功
可以使用Random user來測試是否有正確安裝vue-axios。
到About.vue中,修改如下:
<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
  {{ user }}
</template>
<script>
export default {
  data() {
    return {
      user: {}
    }
  },
  mounted() {
    this.$http.get("https://randomuser.me/api/")
      .then((response) => {
        this.user=response.data.results[0]
        console.log(this.user)
      })
  }
}
</script>
<style>
@media (min-width: 1024px) {
  .about {
    min-height: 100vh;
    display: flex;
    align-items: center;
  }
}
</style>
 
透過vue-axios取得資料