欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

VUE中使用TypeScript裝飾器實(shí)現(xiàn)表單驗(yàn)證的全過程

 更新時(shí)間:2022年03月21日 09:15:19   作者:最后的Hibana  
這篇文章主要給大家介紹了關(guān)于如何在VUE中使用TypeScript裝飾器實(shí)現(xiàn)表單驗(yàn)證的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

前言

最近接觸了關(guān)于很多TypeScript裝飾器的知識,以及class-validator這個(gè)用裝飾器來做表單驗(yàn)證的包,就萌生了想在vue中使用裝飾器來做表單驗(yàn)證的想法。class-validator允許我們在類上通過使用裝飾器來完成表單的驗(yàn)證,并且可在瀏覽器端和node端同時(shí)使用。那么接下來先簡單介紹一下裝飾器和class-validator的用法。

裝飾器

裝飾器的語法十分簡單,只需要在想使用的裝飾器前加上@符號,裝飾器就會(huì)被應(yīng)用到目標(biāo)上。 通過裝飾器我們可以輕松實(shí)現(xiàn)代理模式來使代碼更簡潔以及實(shí)現(xiàn)其它一些更有趣的能力。 關(guān)于裝飾器的用法我用代碼來簡單的舉例幾個(gè),更詳細(xì)的信息大家可自行去網(wǎng)絡(luò)上查找。

// 比如我們有一個(gè)創(chuàng)建用戶的表單,在上面已經(jīng)應(yīng)用了一個(gè)Reactive類裝飾器
// 用了這個(gè)裝飾器后,這個(gè)類實(shí)例出來的對象會(huì)是響應(yīng)式對象了。
@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
  }
}

那么這個(gè)類裝飾器是怎么寫的呢,其實(shí)很簡單

import {reactive} from 'vue-demi'
// vue-demi可以讓你的庫同時(shí)在vue2(@vue/composition-api)和vue3中使用
//下面這個(gè)函數(shù)Reactive就是類裝飾器,返回的是繼承之后的類。實(shí)例化之后返回的是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… 這個(gè)庫給我們的表單加上表單驗(yàn)證

import { IsEmail, IsMobilePhone, Length } from 'class-validator'
@Reactive()
export class CreateUserForm {
    // 下面這些是屬性裝飾器,用來標(biāo)記這些屬性的驗(yàn)條件。
    // 在驗(yàn)證的時(shí)候會(huì)通過Reflect拿到這些元數(shù)據(jù)來驗(yàn)證
    // 我們也可以創(chuàng)建自定義的裝飾器
    @Length(4, 12)
    username: string
    
    @IsEmail()
    email: string

    @IsMobilePhone('zh-CN')
    phone: string

    @Length(4, 12)
    password: string
}

之后在setup中使用,但還是顯得有點(diǎn)粗糙

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,看個(gè)人習(xí)慣
render(){
  const {form,errors} = this
  return <div>
            <div>
                <p>
                    <span>用戶名</span>
                    <input v-model={form.username}></input>
		</p>
                {!!errors.username && <p>錯(cuò)誤提示:{errors.username}</p>}
            </div>
            // ...一些其他表單
            <button onClick={() => this.validate()}>驗(yàn)證</button>
        </div>
}

這里會(huì)有一些需要優(yōu)化的地方

  • 在用的時(shí)候每次需要聲明errors和validate方法,不方便
  • 需要手動(dòng)點(diǎn)擊驗(yàn)證才會(huì)有表單驗(yàn)證
  • 在輸入表單的時(shí)候沒有響應(yīng)式的顯示當(dāng)前字段的錯(cuò)誤提示

封裝Validator

有許多種方法可以優(yōu)化這個(gè),這里我選擇封裝一個(gè)Validator類,有獲取錯(cuò)誤消息和驗(yàn)證的功能,然后讓我們的表單類繼承它。

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>
// 但是代碼提示太不友好了,寫成這樣的話,可以完美的提示
// 這個(gè)接口寫起來很麻煩
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是為了防止跟表單屬性重復(fù)
    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() {
        // 一些驗(yàn)證的代碼
    }
    public clearError() {
      this[ERROR] = {}
    }
    private setError(result: ValidationError[]):Record <string,any > {
       // 將error設(shè)置到this[ERROR]上
    }
    private watchFields(parentKeys ? :string[]) {
       // 這里做了單獨(dú)watch每個(gè)屬性,然后單獨(dú)設(shè)置錯(cuò)誤消息
    }
}
    -----------------------------------------    
//上面watchFields這個(gè)方法需要實(shí)例化的時(shí)候單獨(dú)調(diào)用
//所以我們可以放到Reactive裝飾器上,就不需要再手動(dòng)調(diào)用一次了
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)在我們的代碼可以變成這個(gè)樣子。

// ./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: '姓名長度應(yīng)在2到4間'})
    realName: string

    @IsOptional()
    description?: string
}

// 在表單上可以設(shè)置初始值,現(xiàn)在是固定的。
// 那也可以通過屬性裝飾器調(diào)用api來設(shè)置動(dòng)態(tài)初始值。大家可以自己實(shí)現(xiàn)試試看 
@Reactive()
export class CreateUserForm extends Validator {
    @Length(4, 12, { message: '用戶名長度應(yīng)在4到12間' })
    username: string = ''

    @IsEmail({}, { message: '請?zhí)顚懻_的郵箱' })
    email: string = ''

    @IsMobilePhone('zh-CN', null, { message: '請輸入正確的手機(jī)號碼' })
    phone: string = ''

    @MinLength(4, { message: '密碼長度不應(yīng)低于4' })
    @MaxLength(12, { message: '密碼長度不應(yīng)大于12' })
    password: string = ''

    // 也可以關(guān)聯(lián)其他表單類,但需要下面兩個(gè)裝飾器,用來關(guā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='手機(jī)' v-model={form.phone} error={form.getError().phone}></field>
            <field label='密碼' v-model={form.password} error={form.getError().password}></field>
            <button onClick={() => form.validate()}>驗(yàn)證</button>
            <button onClick={() => form.clearError()}>清空錯(cuò)誤</button>
        </div>
    )
}
})

下面是簡單的頁面演示。

小結(jié)

其實(shí)裝飾器能做的東西很多,也比較好玩。class-validator這個(gè)庫里的裝飾器還有很多,大家可以去github上看看。然而它提供的裝飾器可能并不能完全滿足我們的需求,所以還是需要自己去研究裝飾器,去封裝它。像這種表單,如果要設(shè)置初始值,我們也可以使用裝飾器動(dòng)態(tài)的調(diào)用接口來設(shè)置。

而且我們也許不需要通過繼承的方式來實(shí)現(xiàn)驗(yàn)證器,而是通過將表單類傳入到一個(gè)函數(shù)中,返回驗(yàn)證器。或者把驗(yàn)證器注入到表單中,有很多種方式來實(shí)現(xiàn)。

表單里的裝飾器大多只用到了屬性裝飾器,其實(shí)方法裝飾器也很有意思,類似攔截器,可以在方法調(diào)用前后執(zhí)行你想要的操作,例如設(shè)置loading狀態(tài),完成錯(cuò)誤處理,防抖節(jié)流等等。

如果大家想要嘗試的話可以在vue中npm install vue-class-validator class-validator class-transformer reflect-metadata,或者去(github.com/AndSpark/vu…) 上看看(點(diǎn)個(gè)star)。

到此這篇關(guān)于VUE中使用TypeScript裝飾器實(shí)現(xiàn)表單驗(yàn)證的文章就介紹到這了,更多相關(guān)VUE實(shí)現(xiàn)表單驗(yàn)證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論