VUE中使用TypeScript裝飾器實現表單驗證的全過程
前言
最近接觸了關于很多TypeScript裝飾器的知識,以及class-validator這個用裝飾器來做表單驗證的包,就萌生了想在vue中使用裝飾器來做表單驗證的想法。class-validator允許我們在類上通過使用裝飾器來完成表單的驗證,并且可在瀏覽器端和node端同時使用。那么接下來先簡單介紹一下裝飾器和class-validator的用法。
裝飾器
裝飾器的語法十分簡單,只需要在想使用的裝飾器前加上@符號,裝飾器就會被應用到目標上。 通過裝飾器我們可以輕松實現代理模式來使代碼更簡潔以及實現其它一些更有趣的能力。 關于裝飾器的用法我用代碼來簡單的舉例幾個,更詳細的信息大家可自行去網絡上查找。
// 比如我們有一個創(chuàng)建用戶的表單,在上面已經應用了一個Reactive類裝飾器 // 用了這個裝飾器后,這個類實例出來的對象會是響應式對象了。 @Reactive() export class CreateUserForm { username:string email:string password:string confirmPassword:string }
之后我們在setup中使用它
setup() { // 如果沒有用裝飾器,則需要 const form = reactive(new CreateUserForm()) const form = new CreateUserForm() return { form } }
那么這個類裝飾器是怎么寫的呢,其實很簡單
import {reactive} from 'vue-demi' // vue-demi可以讓你的庫同時在vue2(@vue/composition-api)和vue3中使用 //下面這個函數Reactive就是類裝飾器,返回的是繼承之后的類。實例化之后返回的是reactive對象 function Reactive() { return function <T extends { new (...args: any[]): {} }>(constructor: T){ return class extends constructor { constructor(...args: any[]) { super(...args) return reactive(this) } } } }
class-validator
然后我們通過class-validator(github.com/typestack/c… 這個庫給我們的表單加上表單驗證
import { IsEmail, IsMobilePhone, Length } from 'class-validator' @Reactive() export class CreateUserForm { // 下面這些是屬性裝飾器,用來標記這些屬性的驗條件。 // 在驗證的時候會通過Reflect拿到這些元數據來驗證 // 我們也可以創(chuàng)建自定義的裝飾器 @Length(4, 12) username: string @IsEmail() email: string @IsMobilePhone('zh-CN') phone: string @Length(4, 12) password: string }
之后在setup中使用,但還是顯得有點粗糙
import { validate } from 'class-validator' setup() { const form = new CreateUserForm() const errors = reactive< { [x in keyof CreateUserForm]: string} >({}) const validate = async () => { const err = await validate(form) err.forEach(e => { if (e.constraints) { errors[e.property] = Object.values(e.constraints)[0] } }) } return { form, errors } } // 用的是jsx,看個人習慣 render(){ const {form,errors} = this return <div> <div> <p> <span>用戶名</span> <input v-model={form.username}></input> </p> {!!errors.username && <p>錯誤提示:{errors.username}</p>} </div> // ...一些其他表單 <button onClick={() => this.validate()}>驗證</button> </div> }
這里會有一些需要優(yōu)化的地方
- 在用的時候每次需要聲明errors和validate方法,不方便
- 需要手動點擊驗證才會有表單驗證
- 在輸入表單的時候沒有響應式的顯示當前字段的錯誤提示
封裝Validator
有許多種方法可以優(yōu)化這個,這里我選擇封裝一個Validator類,有獲取錯誤消息和驗證的功能,然后讓我們的表單類繼承它。
import { instanceToPlain } from 'class-transformer' import { validate, ValidationError } from 'class-validator' import { toRef, watch } from 'vue-demi' const ERROR = Symbol('error') const IS_VALID = Symbol('isValid') // 本來我打算error的類型簡單的寫成Record<string,any> // 但是代碼提示太不友好了,寫成這樣的話,可以完美的提示 // 這個接口寫起來很麻煩 type ValidatorError < T > = { [x in Exclude < keyof T, keyof Validator >] ?: T[x] extends PropertyKey ? string: ValidatorRequiredError < T[x] > } type ValidatorRequiredError < T > = { [x in Exclude < keyof T, keyof Validator > ] : T[x] extends PropertyKey ? string | undefined: T[x] extends Function ? T[x] : ValidatorError < T[x] > |undefined } type ValidatorJSON < T > = { [x in Exclude < keyof T, keyof Validator > ] : keyof T extends PropertyKey ? T[x] : ValidatorJSON < T[x] > } export default abstract class Validator { // 這里屬性用symbol是為了防止跟表單屬性重復 private [ERROR]: ValidatorError <this> ={} private [IS_VALID] : boolean = false public getError() { return this[ERROR] } public isValid() { return this[IS_VALID] } public toJSON() { return instanceToPlain(this) as ValidatorJSON < this > } public async validate() { // 一些驗證的代碼 } public clearError() { this[ERROR] = {} } private setError(result: ValidationError[]):Record <string,any > { // 將error設置到this[ERROR]上 } private watchFields(parentKeys ? :string[]) { // 這里做了單獨watch每個屬性,然后單獨設置錯誤消息 } } ----------------------------------------- //上面watchFields這個方法需要實例化的時候單獨調用 //所以我們可以放到Reactive裝飾器上,就不需要再手動調用一次了 function Reactive() { return function <T extends { new (...args: any[]): {} }>(constructor: T){ return class extends constructor { constructor(...args: any[]) { super(...args) const target = reactive(this) if (target.watchFields) { target.watchFields() } return target } } } }
如果將error的類型簡單的寫成Record<string,any>,
要是寫成代碼里的那樣子,
具體的代碼可以到(github.com/AndSpark/vu…) 這里看下。
具體使用
好了,現在我們的代碼可以變成這個樣子。
// ./form.ts import { Type } from 'class-transformer' import { IsEmail, IsMobilePhone, IsOptional, Length, MaxLength, MinLength, ValidateNested } from 'class-validator' import 'reflect-metadata' // 需要引入 reflect-metadata 來使用metadata class Profile { @IsOptional() avatar?: string @Length(2, 4, {message: '姓名長度應在2到4間'}) realName: string @IsOptional() description?: string } // 在表單上可以設置初始值,現在是固定的。 // 那也可以通過屬性裝飾器調用api來設置動態(tài)初始值。大家可以自己實現試試看 @Reactive() export class CreateUserForm extends Validator { @Length(4, 12, { message: '用戶名長度應在4到12間' }) username: string = '' @IsEmail({}, { message: '請?zhí)顚懻_的郵箱' }) email: string = '' @IsMobilePhone('zh-CN', null, { message: '請輸入正確的手機號碼' }) phone: string = '' @MinLength(4, { message: '密碼長度不應低于4' }) @MaxLength(12, { message: '密碼長度不應大于12' }) password: string = '' // 也可以關聯其他表單類,但需要下面兩個裝飾器,用來關聯 @Type(() => Profile) @ValidateNested() profile: Profile = new Profile() }
export default defineComponent({ setup() { const form = new CreateUserForm() return { form } }, render() { const { form } = this return ( <div> <field label='用戶名' v-model={form.username} error={form.getError().username}></field> <field label='姓名' v-model={form.profile.realName} error={form.getError().profile?.realName}></field> <field label='郵箱' v-model={form.email} error={form.getError().email}></field> <field label='手機' v-model={form.phone} error={form.getError().phone}></field> <field label='密碼' v-model={form.password} error={form.getError().password}></field> <button onClick={() => form.validate()}>驗證</button> <button onClick={() => form.clearError()}>清空錯誤</button> </div> ) } })
下面是簡單的頁面演示。
小結
其實裝飾器能做的東西很多,也比較好玩。class-validator
這個庫里的裝飾器還有很多,大家可以去github上看看。然而它提供的裝飾器可能并不能完全滿足我們的需求,所以還是需要自己去研究裝飾器,去封裝它。像這種表單,如果要設置初始值,我們也可以使用裝飾器動態(tài)的調用接口來設置。
而且我們也許不需要通過繼承的方式來實現驗證器,而是通過將表單類傳入到一個函數中,返回驗證器?;蛘甙羊炞C器注入到表單中,有很多種方式來實現。
表單里的裝飾器大多只用到了屬性裝飾器,其實方法裝飾器也很有意思,類似攔截器,可以在方法調用前后執(zhí)行你想要的操作,例如設置loading狀態(tài),完成錯誤處理,防抖節(jié)流等等。
如果大家想要嘗試的話可以在vue中npm install vue-class-validator class-validator class-transformer reflect-metadata
,或者去(github.com/AndSpark/vu…) 上看看(點個star)。
到此這篇關于VUE中使用TypeScript裝飾器實現表單驗證的文章就介紹到這了,更多相關VUE實現表單驗證內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
如何修改element-ui中tree組件的icon圖標(小白都會的前端技能)
這篇文章主要給大家介紹了關于如何修改element-ui中tree組件的icon圖標的相關資料,本文介紹的是小白都會的前端技能,文中通過代碼以及圖文介紹的非常詳細,需要的朋友可以參考下2024-01-01vue?element-ui動態(tài)橫向統(tǒng)計表格的實現
這篇文章主要介紹了vue?element-ui動態(tài)橫向統(tǒng)計表格的實現方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08對vue2.0中.vue文件頁面跳轉之.$router.push的用法詳解
今天小編就為大家分享一篇對vue2.0中.vue文件頁面跳轉之.$router.push的用法詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08