VUE中使用TypeScript裝飾器實現(xiàn)表單驗證的全過程
前言
最近接觸了關于很多TypeScript裝飾器的知識,以及class-validator這個用裝飾器來做表單驗證的包,就萌生了想在vue中使用裝飾器來做表單驗證的想法。class-validator允許我們在類上通過使用裝飾器來完成表單的驗證,并且可在瀏覽器端和node端同時使用。那么接下來先簡單介紹一下裝飾器和class-validator的用法。
裝飾器
裝飾器的語法十分簡單,只需要在想使用的裝飾器前加上@符號,裝飾器就會被應用到目標上。 通過裝飾器我們可以輕松實現(xiàn)代理模式來使代碼更簡潔以及實現(xiàn)其它一些更有趣的能力。 關于裝飾器的用法我用代碼來簡單的舉例幾個,更詳細的信息大家可自行去網(wǎng)絡上查找。
// 比如我們有一個創(chuàng)建用戶的表單,在上面已經(jī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中使用
//下面這個函數(shù)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拿到這些元數(shù)據(jù)來驗證
// 我們也可以創(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…) 這里看下。
具體使用
好了,現(xiàn)在我們的代碼可以變成這個樣子。
// ./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
}
// 在表單上可以設置初始值,現(xiàn)在是固定的。
// 那也可以通過屬性裝飾器調用api來設置動態(tài)初始值。大家可以自己實現(xiàn)試試看
@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 = ''
// 也可以關聯(lián)其他表單類,但需要下面兩個裝飾器,用來關聯(lián)
@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)的調用接口來設置。
而且我們也許不需要通過繼承的方式來實現(xiàn)驗證器,而是通過將表單類傳入到一個函數(shù)中,返回驗證器?;蛘甙羊炞C器注入到表單中,有很多種方式來實現(xiàn)。
表單里的裝飾器大多只用到了屬性裝飾器,其實方法裝飾器也很有意思,類似攔截器,可以在方法調用前后執(zhí)行你想要的操作,例如設置loading狀態(tài),完成錯誤處理,防抖節(jié)流等等。
如果大家想要嘗試的話可以在vue中npm install vue-class-validator class-validator class-transformer reflect-metadata,或者去(github.com/AndSpark/vu…) 上看看(點個star)。
到此這篇關于VUE中使用TypeScript裝飾器實現(xiàn)表單驗證的文章就介紹到這了,更多相關VUE實現(xiàn)表單驗證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
vue實現(xiàn)自定義"模態(tài)彈窗"組件實例代碼
頁面中會有很多時候需要彈窗提示,我們可以寫一個彈窗組件,下面這篇文章主要給大家介紹了關于vue實現(xiàn)自定義"模態(tài)彈窗"組件的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2021-12-12
如何修改element-ui中tree組件的icon圖標(小白都會的前端技能)
這篇文章主要給大家介紹了關于如何修改element-ui中tree組件的icon圖標的相關資料,本文介紹的是小白都會的前端技能,文中通過代碼以及圖文介紹的非常詳細,需要的朋友可以參考下2024-01-01
vue?element-ui動態(tài)橫向統(tǒng)計表格的實現(xiàn)
這篇文章主要介紹了vue?element-ui動態(tài)橫向統(tǒng)計表格的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08
對vue2.0中.vue文件頁面跳轉之.$router.push的用法詳解
今天小編就為大家分享一篇對vue2.0中.vue文件頁面跳轉之.$router.push的用法詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08

