咳咳,各位观众老爷们,晚上好! 今天咱们不聊八卦,专心搞点技术。今天的主题是:如何利用 Vue 的响应式系统和 Composition API
,打造一个既强大又灵活的表单校验库。
开场白:表单校验,前端老生常谈
说起表单校验,简直是前端程序员的家常便饭。 用户输入个手机号,你得看看是不是11位; 用户填个邮箱,你得瞅瞅格式对不对; 用户设个密码,你还得要求强度够不够。
传统的表单校验方式,代码散落在各处,耦合性高,复用性差,维护起来简直让人头大。 想象一下,改一个校验规则,你可能需要翻遍整个项目! 这简直比找对象还难!
所以,我们需要一套优雅、可扩展、可维护的表单校验方案,让我们的代码更清晰,让我们的生活更美好。
第一幕:Vue 的响应式魔法和 Composition API
要打造一个强大的表单校验库,首先要了解 Vue 的两大杀器:响应式系统和 Composition API。
1. Vue 的响应式系统:数据驱动一切
Vue 的响应式系统,简单来说,就是让数据变化自动驱动视图更新。 当我们修改一个响应式数据时,Vue 会自动追踪这个变化,并更新依赖于这个数据的组件。
在表单校验中,我们可以将表单数据定义为响应式数据,然后利用 Vue 的响应式系统,实时监听表单数据的变化,并进行校验。
2. Composition API:化繁为简的代码组织方式
Composition API 是 Vue 3 中引入的一种新的代码组织方式。 它可以让我们将组件的逻辑拆分成一个个独立的函数 (composables),然后在组件中组合使用。
与传统的 Options API 相比,Composition API 更加灵活、可复用、易于测试。 在表单校验库中,我们可以将不同的校验规则封装成独立的 composables,然后在组件中根据需要组合使用。
第二幕:校验库的设计思路
我们的目标是打造一个可扩展、可维护的表单校验库。 因此,我们需要考虑以下几个方面:
- 规则的定义和管理: 如何定义校验规则? 如何管理这些规则?
- 校验的触发时机: 何时触发校验? 是实时校验? 还是提交时校验?
- 错误信息的展示: 如何展示错误信息? 是在输入框下方显示? 还是统一显示?
- 可扩展性: 如何添加新的校验规则? 如何自定义错误信息?
1. 核心数据结构
首先,我们定义一个 ValidationSchema
类型,用于描述每个字段的校验规则:
interface ValidationSchema {
[field: string]: {
rules: ValidationRule[];
};
}
interface ValidationRule {
name: string;
message?: string;
validate: (value: any) => boolean | Promise<boolean>;
}
ValidationSchema
是一个对象,key 是字段名,value 是该字段的校验规则对象。ValidationRule
包含三个属性:name
: 校验规则的名称,例如required
、email
。message
: 错误信息,可以自定义。validate
: 校验函数,接收字段的值作为参数,返回一个布尔值或 Promise,表示校验是否通过。
2. useValidation
composable
接下来,我们创建一个 useValidation
composable,用于处理表单校验的逻辑:
import { reactive, computed, watch } from 'vue';
export function useValidation(schema: ValidationSchema, initialValues: any = {}) {
const values = reactive(initialValues);
const errors = reactive<Record<string, string>>({});
const touched = reactive<Record<string, boolean>>({});
const isValid = computed(() => Object.keys(errors).length === 0);
const isDirty = computed(() => Object.keys(touched).length > 0);
const validateField = async (field: string) => {
const rules = schema[field]?.rules || [];
for (const rule of rules) {
const isValid = await rule.validate(values[field]);
if (!isValid) {
errors[field] = rule.message || `Field ${field} is invalid.`;
return;
}
}
delete errors[field];
};
const validateAll = async () => {
for (const field in schema) {
await validateField(field);
}
};
const handleInputChange = (field: string, value: any) => {
values[field] = value;
touched[field] = true;
validateField(field); // 实时校验
};
const resetForm = () => {
for (const field in values) {
values[field] = initialValues[field] || '';
delete errors[field];
delete touched[field];
}
};
watch(() => values, () => {
// 监听所有值的变化,可以做一些全局性的逻辑
}, { deep: true });
return {
values,
errors,
touched,
isValid,
isDirty,
validateField,
validateAll,
handleInputChange,
resetForm,
};
}
这个 useValidation
composable 接收两个参数:
schema
:ValidationSchema
对象,描述了表单的校验规则。initialValues
: 表单的初始值,可选。
它返回以下属性和方法:
values
: 响应式的表单数据对象。errors
: 响应式的错误信息对象,key 是字段名,value 是错误信息。touched
: 响应式的 touched 状态对象,key 是字段名,value 是布尔值,表示该字段是否被 touched。isValid
: 计算属性,表示表单是否有效。isDirty
: 计算属性,表示表单是否被修改过。validateField
: 用于校验单个字段的函数。validateAll
: 用于校验所有字段的函数。handleInputChange
: 用于处理输入框变化的函数,它会更新表单数据,并触发校验。resetForm
: 用于重置表单的函数。
3. 默认校验规则
为了方便使用,我们可以提供一些常用的校验规则:
const required: ValidationRule = {
name: 'required',
message: 'This field is required.',
validate: (value: any) => !!value,
};
const email: ValidationRule = {
name: 'email',
message: 'Please enter a valid email address.',
validate: (value: any) => {
if (!value) return true; // 允许为空
const regex = /^[^s@]+@[^s@]+.[^s@]+$/;
return regex.test(value);
},
};
const minLength = (length: number): ValidationRule => ({
name: 'minLength',
message: `This field must be at least ${length} characters.`,
validate: (value: any) => !value || value.length >= length, // 允许为空
});
const maxLength = (length: number): ValidationRule => ({
name: 'maxLength',
message: `This field cannot be longer than ${length} characters.`,
validate: (value: any) => !value || value.length <= length, // 允许为空
});
const pattern = (regex: RegExp, message: string): ValidationRule => ({
name: 'pattern',
message: message,
validate: (value: any) => !value || regex.test(value), // 允许为空
});
// 可以根据需要添加更多规则,例如数字、URL、自定义规则等等
export const Rules = {
required,
email,
minLength,
maxLength,
pattern,
};
这些规则都是一些简单的函数,接收字段的值作为参数,返回一个布尔值,表示校验是否通过。
第三幕:实战演练
现在,让我们用一个简单的例子来演示如何使用这个表单校验库。
<template>
<div>
<label for="name">Name:</label>
<input type="text" id="name" v-model="validation.values.name" @blur="validation.touched.name = true">
<div v-if="validation.touched.name && validation.errors.name" class="error">{{ validation.errors.name }}</div>
<label for="email">Email:</label>
<input type="email" id="email" v-model="validation.values.email" @blur="validation.touched.email = true">
<div v-if="validation.touched.email && validation.errors.email" class="error">{{ validation.errors.email }}</div>
<button @click="handleSubmit" :disabled="!validation.isValid">Submit</button>
<button @click="validation.resetForm">Reset</button>
</div>
</template>
<script setup lang="ts">
import { useValidation, Rules } from './validation'; // 假设 validation.ts 包含 useValidation 和 Rules
const schema = {
name: {
rules: [Rules.required, Rules.minLength(3), Rules.maxLength(10)],
},
email: {
rules: [Rules.required, Rules.email],
},
};
const validation = useValidation(schema, { name: '', email: '' });
const handleSubmit = async () => {
await validation.validateAll();
if (validation.isValid.value) {
// 表单提交逻辑
alert('Form is valid!');
} else {
alert('Form is invalid!');
}
};
</script>
<style scoped>
.error {
color: red;
}
</style>
在这个例子中,我们定义了一个包含 name
和 email
两个字段的表单。
name
字段要求必填,且长度在 3 到 10 个字符之间。email
字段要求必填,且必须是有效的邮箱地址。
我们使用 useValidation
composable 来处理表单校验的逻辑。
v-model
指令将输入框的值绑定到validation.values
对象上。@blur
事件监听输入框失去焦点事件,并将对应的validation.touched
属性设置为true
。v-if
指令用于显示错误信息。handleSubmit
函数用于提交表单,它会先调用validation.validateAll
函数校验所有字段,如果表单有效,则执行提交逻辑。resetForm
函数用于重置表单。
第四幕:可扩展性设计
一个好的表单校验库,不仅要功能强大,还要易于扩展。 我们可以通过以下方式来提高校验库的可扩展性:
- 自定义校验规则: 允许用户自定义校验规则。
- 自定义错误信息: 允许用户自定义错误信息。
- 异步校验: 支持异步校验,例如从服务器端获取校验结果。
- 国际化支持: 支持国际化,可以显示不同语言的错误信息。
1. 自定义校验规则
用户可以通过 addRule
函数来添加自定义校验规则:
// 在 useValidation 内部
const addRule = (field: string, rule: ValidationRule) => {
if (!schema[field]) {
schema[field] = { rules: [] };
}
schema[field].rules.push(rule);
};
return {
// ... 其他属性和方法
addRule,
};
使用方法:
const validation = useValidation(schema, { name: '', email: '' });
validation.addRule('name', {
name: 'custom',
message: 'Name cannot contain numbers.',
validate: (value: any) => !/d/.test(value),
});
2. 自定义错误信息
用户可以在定义校验规则时,自定义错误信息:
const schema = {
name: {
rules: [
{ ...Rules.required, message: 'Please enter your name.' },
{ ...Rules.minLength(3), message: 'Name must be at least 3 characters.' },
],
},
email: {
rules: [
{ ...Rules.required, message: 'Please enter your email address.' },
{ ...Rules.email, message: 'Please enter a valid email address.' },
],
},
};
3. 异步校验
如果校验规则需要从服务器端获取校验结果,可以使用异步校验。
const usernameAvailable: ValidationRule = {
name: 'usernameAvailable',
message: 'Username is not available.',
validate: async (value: any) => {
if (!value) return true; // 允许为空
// 从服务器端获取校验结果
const response = await fetch(`/api/check-username?username=${value}`);
const data = await response.json();
return data.available;
},
};
4. 国际化支持
可以使用 Vue I18n 等国际化库来实现国际化支持。
第五幕:总结与展望
通过 Vue 的响应式系统和 Composition API,我们成功打造了一个可扩展、可维护的表单校验库。
这个校验库具有以下优点:
- 代码清晰: 使用 Composition API 将校验逻辑封装成独立的 composables,代码更加清晰易懂。
- 可复用性高: 校验规则可以复用在不同的组件中。
- 易于测试: 独立的 composables 易于进行单元测试。
- 可扩展性强: 可以方便地添加新的校验规则和自定义错误信息。
当然,这个校验库还有很多可以改进的地方,例如:
- 更丰富的校验规则: 可以提供更多常用的校验规则,例如数字、URL、自定义规则等等。
- 更灵活的错误信息展示方式: 可以提供更多的错误信息展示方式,例如在输入框上方显示、统一显示等等。
- 更强大的异步校验功能: 可以支持取消异步校验请求、显示 loading 状态等等。
希望通过今天的讲座,能够帮助大家更好地理解 Vue 的响应式系统和 Composition API,并能够利用这些技术打造出更强大的表单校验库。
感谢各位的观看! 下次再见!