安装 reflect-metadata
npm i reflect-metadata
tsconfig.json中配置
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"experimentalDecorators": true,//支持装饰器写法
"emitDecoratorMetadata": true,//支持Reflect的Metadata扩展
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
使用装饰器
import 'reflect-metadata'
class Err {
msg
constructor(msg = '不能为空') {
this.msg = msg
}
}
// 自定义属性装饰器 IsNotEmpty
function IsNotEmpty(target: any, propertyName: string) {
Reflect.defineMetadata('IsNotEmpty', true, target, propertyName)
}
// 校验函数
function validateNotEmpty(
target: any,
_propertyName: string,
descriptor: PropertyDescriptor
) {
//缓存方法原来的值
const method = descriptor.value
//对方法更改并执行
descriptor.value = function (...args: any[]) {
for (const key in this) {
const needVal = Reflect.getMetadata('IsNotEmpty', target, key)
if (needVal) {
const val = this[key as keyof PropertyDescriptor]
if (!val || (typeof val === 'string' && !val.trim())) {
throw new Err(`${key}不能为空`)
}
}
}
//在最后执行原来的方法
return method.apply(this, args)
}
}
// 使用装饰器
class Example {
@IsNotEmpty
public name: string
constructor(name: string) {
this.name = name
}
@validateNotEmpty
public greet() {
console.log(`Hello, ${this.name}!`)
}
}
// 测试,此时name为空,会捕获到错误
const example = new Example(' ')
try {
example.greet()
} catch (err) {
console.log(err)
}
在vue中使用装饰器创造响应式对象
import { reactive } from 'vue'
import 'reflect-metadata'
class Err {
msg
constructor(msg = '不能为空') {
this.msg = msg
}
}
// 自定义类装饰器 Reactive
function Reactive<T extends { new (...args: any[]): {} }>(my_constructor: T) {
return class extends my_constructor {
constructor(...args: any[]) {
super(...args)
//把类的实例变成响应式的
return reactive(this) //返回的是对象,该对象会覆盖constructor返回的对象实例
}
}
}
function IsNotEmpty(target: any, propertyName: string) {
Reflect.defineMetadata('IsNotEmpty', true, target, propertyName)
}
function validateNotEmpty(
target: any,
_propertyName: string,
descriptor: PropertyDescriptor
) {
const method = descriptor.value
descriptor.value = function (...args: any[]) {
for (const key in this) {
const needVal = Reflect.getMetadata('IsNotEmpty', target, key)
if (needVal) {
const val = this[key as keyof PropertyDescriptor]
if (!val || (typeof val === 'string' && !val.trim())) {
throw new Err(`${key}不能为空`)
}
}
}
return method.apply(this, args)
}
}
// 使用装饰器
@Reactive
class Example {
@IsNotEmpty
name: string
constructor(name: string) {
this.name = name
}
@validateNotEmpty
greet() {
console.log(`Hello, ${this.name}!`)
}
}
此时,类Example的实例对象就变成了响应式的。修改其实例对象的属性值时,在页面上也会发生变化
给响应式对象赋值
使用自带的校验规则
import { showToast } from 'vant'
import { Validator, validate } from 'class-validator'
/**
* 自定义表单异常
* @constructor (message)自定义异常
*/
export class FormException extends Error {
constructor(message: string) {
super(message)
this.name = 'FormException'
}
}
/**
* 初始化监听异常的方法
*/
export const ExceptionInterceptor = () => {
window.addEventListener('error', function (event) {
if (event.error instanceof FormException) {
showToast(event.error.message)
}
})
window.addEventListener('unhandledrejection', (event) => {
const { reason } = event
if (reason instanceof FormException) {
showToast(reason.message)
}
})
}
//将它的子类用@Reactive变成响应式的类,就可以使用这些方法给响应式对象赋值了
export class FormValidator extends Validator {
[key: string]: any
//初始化
init<T extends Record<string, any>>(form: T) {
for (const key in form) {
if (Object.prototype.hasOwnProperty.call(this, key)) {
this[key] = form[key] as any
}
}
}
//重置各个属性的值
reset() {
const instance = Reflect.construct(this.constructor, [])
const keys = Object.keys(instance)
keys.forEach((key) => {
this[key] = instance[key]
})
}
//表单提交
submit(): Promise<this> {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
const err = await validate(this)
if (err.length > 0) {
//需要配合class-validator中的验证注解使用,str为验证失败的提示信息
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const str = Object.values(err[0].constraints!)[0]
throw new FormException(str)
}
resolve(this)
})
}
}
自定义校验
import {
IsNotEmpty,
Validate,
ValidatorConstraint,
ValidatorConstraintInterface
} from 'class-validator'
import { Reactive } from '@/utils/decorator.ts'
import { FormValidator } from '@/utils/FormException.ts'
@ValidatorConstraint()
export class ArrayEachNotEmpty implements ValidatorConstraintInterface {
validate(value: string[]) {
if (!Array.isArray(value)) return false
return value[0] != ''
}
defaultMessage() {
return '错误'
}
}
@Reactive()
export class UploadCaseDto extends FormValidator {
@IsNotEmpty({ message: '请输入' })
not_empty= ''
@Validate(ArrayEachNotEmpty)
my_array = ['', '', '']
}