用Typescript 的方式封裝Vue3的表單綁定,支持防抖等功能

在 Vue3 中結合 TypeScript 封裝表單綁定方案時,需要綜合考慮類型安全、功能擴展性和開發體驗。以下是一個包含防抖功能、支持多種表單控件、具備完整類型推導的封裝方案,全文約 2300 字:

方案設計思路

  1. 組合式函數封裝:使用 Vue3 的 setup 語法和 Composition API
  2. 類型泛型支持:通過 TypeScript 泛型實現表單數據的強類型約束
  3. 防抖功能集成:內置可配置的防抖邏輯,支持異步操作
  4. 多控件支持:統一處理 input/textarea/select/checkbox 等常見表單元素
  5. 驗證機制擴展點:預留自定義驗證邏輯的接入接口

完整實現代碼

import { ref, watch, computed, Ref } from 'vue';
import type { MaybeRefOrGetter, ComputedRef } from 'vue';// 防抖函數實現(兼容異步)
function debounce<T extends (...args: any[]) => any>(func: T,wait: number,immediate?: boolean
): (...args: Parameters<T>) => void {let timeout: ReturnType<typeof setTimeout> | null = null;return (...args: Parameters<T>) => {const callNow = immediate && !timeout;const later = () => {timeout = null;if (!immediate) func.apply(this, args);};clearTimeout(timeout!);timeout = setTimeout(later, wait);if (callNow) func.apply(this, args);};
}// 表單字段配置接口
interface FormFieldConfig<T = any> {value?: T;debounce?: number;validator?: (value: T) => string | boolean;transform?: (value: T) => any;
}// 表單狀態管理類
class FormState<T extends Record<string, any>> {private _rawValues: T;private fields: Record<keyof T, FormFieldConfig>;private _errors: Ref<Record<string, string>> = ref({});private _isValid: ComputedRef<boolean> = computed(() => Object.values(this._errors.value).every(msg => !msg));constructor(initialValues: T, fieldConfigs: Record<keyof T, FormFieldConfig> = {}) {this._rawValues = initialValues;this.fields = fieldConfigs;this.initFields();}private initFields() {Object.keys(this._rawValues).forEach(key => {if (this.fields[key as keyof T]?.debounce) {const debounceTime = this.fields[key as keyof T]!.debounce!;const originalSetter = (val: any) => {this._rawValues[key as keyof T] = val;};this._rawValues[key as keyof T] = new Proxy(this._rawValues[key as keyof T], {set: debounce((_, prop, newValue) => {originalSetter(newValue);}, debounceTime)});}});}public get values(): T {return this._rawValues;}public get errors(): Record<string, string> {return this._errors.value;}public get isValid(): boolean {return this._isValid.value;}public validateField(field: keyof T): boolean {const config = this.fields[field];const value = this._rawValues[field];if (config?.validator) {const result = config.validator(value);this._errors.value[field as string] = typeof result === 'string' ? result : result ? '' : 'Invalid value';}return !this._errors.value[field as string];}public validateAll(): boolean {let isValid = true;Object.keys(this._rawValues).forEach(key => {if (!this.validateField(key as keyof T)) isValid = false;});return isValid;}public reset(): void {this._rawValues = Object.keys(this._rawValues).reduce((acc, key) => ({ ...acc, [key]: this.fields[key as keyof T]?.value || '' }),{} as T);this._errors.value = {};}
}// 組合式函數
export function useForm<T extends Record<string, any>>(initialValues: T,fieldConfigs?: Record<keyof T, FormFieldConfig>
) {const formState = new FormState(initialValues, fieldConfigs);// 創建響應式表單值const formValues = computed({get: () => formState.values,set: (newValues) => {Object.assign(formState.values, newValues);}});// 字段綁定函數function useField<K extends keyof T>(field: K) {const value = computed({get: () => formValues.value[field],set: (newValue) => {formValues.value[field] = newValue;}});const error = computed(() => formState.errors[field as string]);return {value,error,validate: () => formState.validateField(field)};}// 表單提交處理async function handleSubmit<R = any>(submitFn: (values: T) => Promise<R>,options?: { validate?: boolean }): Promise<R | null> {if (options?.validate && !formState.validateAll()) return null;try {const result = await submitFn(formValues.value);formState.reset();return result;} catch (e) {console.error('Form submission error:', e);return null;}}return {values: formValues,errors: computed(() => formState.errors),isValid: computed(() => formState.isValid),useField,validate: () => formState.validateAll(),reset: () => formState.reset(),submit: handleSubmit};
}// 類型導出
export type FormFieldConfig = FormFieldConfig;
export type FormStateType<T> = FormState<T>;

使用示例

<script setup lang="ts">
import { useForm } from '@/composables/useForm';interface LoginForm {email: string;password: string;remember: boolean;
}const formConfig = {email: {debounce: 500,validator: (value: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ? true : 'Please enter a valid email address'},password: {debounce: 300,validator: (value: string) => value.length >= 8 ? true : 'Password must be at least 8 characters'}
};const { values, errors, isValid, useField, submit } = useForm<LoginForm>({email: '',password: '',remember: false}, formConfig);const email = useField('email');
const password = useField('password');
const remember = useField('remember');const handleSubmit = submit(async (values) => {console.log('Form submitted:', values);// 這里調用APIreturn { success: true };
});
</script><template><form @submit.prevent="handleSubmit"><div><label>Email</label><input v-model="email.value" type="email"><div v-if="email.error" class="error">{{ email.error }}</div></div><div><label>Password</label><input v-model="password.value" type="password"><div v-if="password.error" class="error">{{ password.error }}</div></div><div><input v-model="remember.value" type="checkbox" id="remember"><label for="remember">Remember me</label></div><button type="submit" :disabled="!isValid">Submit</button></form>
</template>

方案特點分析

  1. 強類型支持

    • 使用 TypeScript 泛型確保表單數據的類型安全
    • 字段配置與驗證函數類型約束
    • 組合式函數返回值類型明確
  2. 防抖功能

    • 通過 Proxy 實現動態屬性攔截
    • 可配置的防抖時間(字段級配置)
    • 支持異步驗證場景
  3. 驗證系統

    • 內置驗證器接口
    • 字段級錯誤收集
    • 表單整體有效性狀態
    • 支持自定義驗證邏輯
  4. 擴展性設計

    • 表單狀態管理類封裝核心邏輯
    • 組合式函數暴露友好API
    • 預留 transform 等數據處理鉤子
    • 支持異步提交處理
  5. 開發體驗優化

    • 符合 Vue 響應式設計哲學
    • 字段綁定自動推導類型
    • 錯誤信息自動收集展示
    • 表單提交自動重置

性能優化點

  1. 防抖內存管理:使用 Proxy 替代直接屬性替換,避免重復創建對象
  2. 計算屬性緩存:合理使用 computed 減少不必要計算
  3. 批量更新:表單提交時使用 Object.assign 批量更新
  4. 錯誤狀態隔離:每個字段錯誤狀態獨立存儲,避免全局遍歷

擴展方向建議

  1. 集成第三方驗證庫:如 Vuelidate、Yup 等
  2. 添加字段掩碼:支持密碼顯示切換、敏感信息脫敏
  3. 表單版本管理:實現表單狀態的歷史記錄
  4. 可視化校驗提示:集成 UI 框架的表單驗證樣式
  5. 國際化支持:多語言錯誤提示

本方案通過 TypeScript 泛型和 Vue3 組合式 API 的結合,實現了類型安全、功能豐富的表單綁定解決方案。防抖功能的集成既減少了不必要的驗證觸發,又提升了表單的響應性能,同時保持了代碼的可維護性和擴展性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/922040.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/922040.shtml
英文地址,請注明出處:http://en.pswp.cn/news/922040.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

中悅大華通過訂單日記實現流程重構之路

一、客戶背景 安徽中悅大華高速流體機械有限公司&#xff0c;成立于2023年&#xff0c;位于安徽省宣城市&#xff0c;是一家以從事電子設備制造為主的企業&#xff0c;在多年的商業經營中已成為業界翹楚。 在業務不斷壯大的過程中&#xff0c;面臨生產協作效率低&#xff0c;庫…

【Springboot】介紹啟動類和啟動過程

【Springboot】介紹啟動類和啟動過程【一】Spring Boot 啟動類的注解【1】核心注解&#xff1a;SpringBootApplication&#xff08;1&#xff09;?SpringBootConfiguration?&#xff1a;Spring容器會從該類中加載Bean定義&#xff08;2&#xff09;?EnableAutoConfiguration…

Gears實測室:第一期·音游跨設備性能表現與工具價值實踐

在音游品類中&#xff0c;《跳舞的線》以 “音樂與操作節奏深度綁定” 的玩法特性&#xff0c;對設備性能提出了特殊要求 —— 穩定的幀率與低延遲的渲染響應&#xff0c;直接影響玩家對音符時機的判斷&#xff0c;一旦出現卡頓或幀波動&#xff0c;易導致操作失誤&#xff0c;…

格式刷+快捷鍵:Excel和WPS表格隔行填充顏色超方便

有時候我們會對Excel或WPS表格的數據區域每隔一行填充一個底紋&#xff0c;便于閱讀和查看。可以使用條件格式搭配公式實現&#xff0c;也可以手動設置。通常手動設置的時候是先設置一行&#xff0c;然后再雙擊格式刷應用。可以有更快的方式&#xff1a;先設置一行底紋&#xf…

將現有Spring Boot項目作為模塊導入到另一個Spring Boot項目

將現有Spring Boot項目作為模塊導入到另一個Spring Boot項目的操作步驟如下&#xff1a;?項目結構調整?將待導入的項目文件夾復制到主項目的根目錄下修改子模塊目錄名保持命名規范&#xff08;如ms-xxx格式&#xff09;?父POM配置?在主項目的pom.xml中添加<modules>聲…

激光頻率梳 3D 輪廓測量 - 銑刀刀片的刀口鈍化值 R 的測量

一、引言銑刀刀片的刀口鈍化值 R 是影響切削性能的關鍵參數&#xff0c;其精度直接關系到工件表面質量與刀具壽命。傳統測量方法在面對微米級鈍化圓角時存在分辨率不足、接觸式測量易損傷刃口等問題。激光頻率梳 3D 輪廓測量技術憑借飛秒級時頻基準與亞微米級測量精度&#xff…

3-10〔OSCP ? 研記〕? WEB應用攻擊?XSS攻擊理論基礎

鄭重聲明&#xff1a; 本文所有安全知識與技術&#xff0c;僅用于探討、研究及學習&#xff0c;嚴禁用于違反國家法律法規的非法活動。對于因不當使用相關內容造成的任何損失或法律責任&#xff0c;本人不承擔任何責任。 如需轉載&#xff0c;請注明出處且不得用于商業盈利。 …

《嵌入式硬件(四):溫度傳感器DS1820》

一、DS1820的引腳DS1820單總線數字溫度計&#xff1a;異步串行半雙工特性&#xff1a;1&#xff09;獨特的單線接口&#xff0c;只需 1 個接口引腳即可通信2&#xff09;多點&#xff08;multidrop&#xff09;能力使分布式溫度檢測應用得以簡化3&#xff09;不需要外部元件4&a…

langchain 輸出解析器 Output Parser

示例中使用的公共代碼&#xff1a; from langchain_deepseek import ChatDeepSeek chat ChatDeepSeek(model"deepseek-chat",temperature0,api_keyAPI_KEY, )使用方法&#xff1a; 引入解析器實例化解析器調用解析器的get_format_instructions()獲得提示詞&#xff…

LeetCode算法日記 - Day 37: 驗證棧序列、N叉樹的層序遍歷

目錄 1. 驗證棧序列 1.1 題目解析 1.2 解法 1.3 代碼實現 2. N叉樹的層序遍歷 2.1 題目解析 2.2 解法 2.3 代碼實現 1. 驗證棧序列 https://leetcode.cn/problems/validate-stack-sequences/description/ 給定 pushed 和 popped 兩個序列&#xff0c;每個序列中的 值…

金融數據庫--3Baostock

一、 Baostock 是什么&#xff1f;Baostock&#xff08;寶碩股票&#xff09;是一個免費、開源的證券數據平臺&#xff08;SDK&#xff09;&#xff0c;旨在為金融量化投資者、研究人員和學生提供穩定、準確、易用的A股歷史數據和相關金融數據。其核心是一個 Python 庫&#xf…

微信小程序-1-微信開發者工具環境搭建和初始化創建項目

文章目錄1 小程序概述1.1 什么是微信小程序1.2 大前端概念1.3 賬號注冊1.4 開發流程1.5 小程序成員2 創建項目2.1 創建項目流程2.2 創建項目2.3 本地開發支持http3 項目目錄3.1 項目目錄結構3.2 配置文件3.2.1 app.json(全局配置)3.2.2 xxx.json(頁面配置)3.2.3 project.config…

Go語言開發AI應用

為什么選擇Go語言開發AI應用在人工智能快速發展的今天&#xff0c;選擇合適的編程語言對于AI應用的成功至關重要。雖然Python長期以來被認為是AI開發的首選語言&#xff0c;但Go語言正在逐漸嶄露頭角&#xff0c;成為AI應用開發的有力競爭者。Go語言的核心優勢1. 卓越的性能表現…

10. 游戲開發中的TCP與UDP

1.TCP和UDP 2.TCP為什么慢于UDP 3.可靠UDP1.TCP和UDP 1).通過打電話的方式說明TCP和UDPa.TCP(傳輸控制協議), 就像打電話- 需要先撥號, 接通, 問候(建立連接)- 你一句, 我一句, 對方沒有聽清會要求你重復(確認與重傳)- 保證對話有條不紊, 內容準確無誤(可靠, 有序)- 如果信號不…

CMap常用函數

CMap 是 MFC 中用于存儲鍵值對&#xff08;key-value&#xff09;的關聯容器類&#xff0c;類似于 C 標準庫中的 std::map&#xff0c;但依賴 MFC 框架實現。它采用哈希表&#xff08;Hash Table&#xff09;作為底層數據結構&#xff0c;支持高效的鍵值查找、插入和刪除操作。…

Rocky9.0去堆疊雙發arp(支持“ARP 廣播雙發”)

摘要 在去堆疊/MLAG 場景下&#xff0c;默認 bonding 只會以單口回復 ARP&#xff0c;另一臺交換機收不到 ARP Reply。本文在 Linux bonding 驅動中增加參數 arp_broadcast_mode&#xff0c;當開啟時對 ARP 包臨時切換到 廣播模式&#xff0c;實現雙口同時發 ARP Reply。文內提…

網頁連接攝像頭

攝像機處理 <!-- camera_solve.html --> <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>…

FPGA雷達信號處理之:自適應門限閾值

一、原理 參考這個博主&#xff0c;講的很仔細&#xff1a;基于脈沖功率的雷達脈沖參數檢測原理詳解 二、FPGA實現 使用system generator搭建算法模型如下&#xff1a; 在這里&#xff0c;濾波器窗長度為8&#xff0c;原博主設置為50效果更好&#xff0c;門限公式如下&#xf…

Vue 中實現選中文本彈出彈窗的完整指南

在現代 Web 應用中&#xff0c;選中文本后顯示相關操作或信息是一種常見的交互模式。本文將詳細介紹如何在 Vue 中實現選中文本后彈出彈窗的功能&#xff0c;包括其工作原理、多種實現方式以及實際項目中的應用示例。 一、實現原理 1. 文本選中檢測機制 瀏覽器提供了 Select…

第4節-排序和限制-FETCH

摘要: 在本教程中&#xff0c;你將學習如何使用 PostgreSQL 的 FETCH 子句從查詢中檢索部分行。 PostgreSQL FETCH 簡介 在 PostgreSQL 中&#xff0c;OFFSET 子句的作用類似于 LIMIT 子句。FETCH 子句允許你限制查詢返回的行數。 LIMIT 子句并非 SQL 標準的一部分。不過&#…