日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

组件库实战 | 教你如何设计Web世界中的表单验证

發布時間:2023/12/4 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 组件库实战 | 教你如何设计Web世界中的表单验证 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

教你如何設計Web世界中的表單驗證

  • 💬序言
  • 🗯?一、驗證輸入框ValidateInput
    • 1. 設計稿搶先知
    • 2. 簡單的實現
    • 3. 抽象驗證規則
    • 4. v-model
    • 5. 使用$attrs支持默認屬性
  • 💭二、驗證表單ValidateForm
    • 1. 組件需求分析
    • 2. 使用插槽 slot
    • 3. 父子組件通訊
  • 👁??🗨?四、結束語
  • 💯 往期推薦

💬序言

在實際開發中,我們有一個很經常開發的場景,那就是登錄注冊。登錄注冊實際上涉及到的內容是表單驗證,因此呢,表單驗證也是 web 世界中一個很重要的功能。

那接下里就來了解,在實際的開發中,如何更規范合理地去開發一個表單驗證,使其擴展性更強,邏輯更加清晰。

一起來學習⑧~

🗯?一、驗證輸入框ValidateInput

1. 設計稿搶先知

在了解具體的實現方式之前,我們首先來看原型圖。看我們想要實現的表單是怎么樣的。如下圖所示:

大家可以看到,用我們最熟悉的表單驗證就是登錄注冊操作。其中,整個表單包含四部分。

第一部分是紅色框框的內容,紅色框框想要做的事情就是,當元素失去焦點時候去觸發事件。

第二部分是驗證規則,我們不管是在輸入用戶名還是密碼,都需要校驗規則來進行校驗,比如說不為空,限制輸入長度等等內容。

第三部分是當驗證沒有通過時,需要出現具體的警告。

第四部分就是當所有內容都輸入并且要進行提交時,要去驗證整個 Form 表單。

2. 簡單的實現

我們先來給表單進行一個簡單的實現。現在我們在 vue3 項目中的 App.vue 下對整個表單先進行渲染,并且對郵箱的邏輯進行編寫。具體代碼如下:

<template><div class="container"><global-header :user="user"></global-header><form action=""><div class="mb-3"><label for="exampleInputEmail1" class="form-label">郵箱地址</label><inputtype="email" class="form-control" id="exampleEmail1"v-model="emailRef.val"@blur="validateEmail"><div class="form-text" v-if="emailRef.error">{{emailRef.message}}</div></div><div class="mb-3"><label for="exampleInputPassword1" class="form-label">密碼</label><input type="password" class="form-control" id="exampleInputPassword1"></div></form></div> </template><script lang="ts"> import { defineComponent, reactive, ref } from 'vue' import 'bootstrap/dist/css/bootstrap.min.css' import ColumnList, { ColumnProps } from './components/ColumnList.vue' import GlobalHeader, { UserProps } from './components/GlobalHeader.vue' const currentUser: UserProps = {isLogin: true,name: 'Monday' } // 判斷是否是郵箱的格式 const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/ const testData: ColumnProps[] = [{id: 1,title: 'test1專欄',description: '眾所周知, js 是一門弱類型語言,并且規范較少。這就很容易導致在項目上線之前我們很難發現到它的錯誤,等到項目一上線,渾然不覺地,bug就UpUp了。于是,在過去的這兩年,ts悄悄的崛起了。 本專欄將介紹關于ts的一些學習記錄。'// avatar: 'https://img0.baidu.com/it/u=3101694723,748884042&fm=26&fmt=auto&gp=0.jpg'},{id: 2,title: 'test2專欄',description: '眾所周知, js 是一門弱類型語言,并且規范較少。這就很容易導致在項目上線之前我們很難發現到它的錯誤,等到項目一上線,渾然不覺地,bug就UpUp了。于是,在過去的這兩年,ts悄悄的崛起了。 本專欄將介紹關于ts的一些學習記錄。',avatar: 'https://img0.baidu.com/it/u=3101694723,748884042&fm=26&fmt=auto&gp=0.jpg'} ]export default defineComponent({name: 'App',components: {GlobalHeader},setup () {// 郵箱驗證部分數據內容const emailRef = reactive({val: '',error: false,message: ''})// 驗證郵箱邏輯const validateEmail = () => {// .trim 表示去掉兩邊空格// 當郵箱為空時if (emailRef.val.trim() === '') {emailRef.error = trueemailRef.message = 'can not be empty'} // 當郵箱不為空,但它不是有效的郵箱格式時else if (!emailReg.test(emailRef.val)) {emailRef.error = trueemailRef.message = 'should be valid email'}}return {list: testData,user: currentUser,emailRef,validateEmail}} }) </script>

現在,我們來看下具體的顯示效果:

好了,現在我們第一步就實現啦!那么接下來,我們是不是就應該來寫 password 的邏輯了呢?

但是啊,如果按照上面這種方式來寫的話,有小伙伴會不會覺得就有點重復操作了呢。一兩個校驗規則還好,如果我們遇到十幾二十個呢?也一樣每一個都這么寫嗎?

答案當然是否定的。那么下一步,我們就要對這個校驗規則,來進行抽象。

3. 抽象驗證規則

繼續,我們現在要來抽象出用戶名和密碼的校驗規則,讓其可擴展性更強。具體形式如下:

<validate-input :rules="" />interface RuleProp {type: 'required' | 'email' | 'range' | ...;message: string; } export type RulesProp = RuleProp[]

首先,我們要先把表單組件給抽離出來。那么現在,我們在 vue3 項目下的 src|components 下創建一個文件,命名為 ValidateInput.vue 。其具體代碼如下:

<template><div class="validate-input-container pb-3"><!-- 手動處理更新和發送事件 --><!-- 使用可選 class,用于動態計算類名 --><input type="text"class="form-control":class="{'is-invalid': inputRef.error}"v-model="inputRef.val"@blur="validateInput"><span v-if="inputRef.error" class="invalid-feedback">{{inputRef.message}}</span></div> </template><script lang="ts"> import { defineComponent, reactive, PropType } from 'vue' // 判斷email的正則表達式 const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/ // required表示必填值,email表示電子郵件的格式 // message用來展示當出現問題時提示的錯誤 interface RuleProp {type: 'required' | 'email';message: string;validator?: () => boolean; }export type RulesProp = RuleProp[] export default defineComponent({name: 'ValidateInput',props: {// 用PropType來確定rules的類型,明確里面是RulesProp// 這里的rules數據將被父組件 App.vue 給進行動態綁定rules: Array as PropType<RulesProp>},setup(props, context) {// 輸入框的數據const inputRef = reactive({val: '',error: false,message: ''})// 驗證輸入框const validateInput = () => {if (props.rules) {const allPassed = props.rules.every(rule => {let passed = trueinputRef.message = rule.messageswitch (rule.type) {case 'required':passed = (inputRef.val.trim() !== '')breakcase 'email':passed = emailReg.test(inputRef.val)breakdefault:break}return passed})inputRef.error = !allPassed}}return {inputRef,validateInput}} }) </script><style></style>

之后我們將其在 App.vue 下進行注冊。具體代碼如下:

<template><div class="container"><global-header :user="user"></global-header><form action=""><div class="mb-3"><label class="form-label">郵箱地址</label><validate-input :rules="emailRules"></validate-input></div><div class="mb-3"><label for="exampleInputEmail1" class="form-label">郵箱地址</label><inputtype="email" class="form-control" id="exampleEmail1"v-model="emailRef.val"@blur="validateEmail"><div class="form-text" v-if="emailRef.error">{{emailRef.message}}</div></div><div class="mb-3"><label for="exampleInputPassword1" class="form-label">密碼</label><input type="password" class="form-control" id="exampleInputPassword1"></div></form></div> </template><script lang="ts"> import { defineComponent, reactive, ref } from 'vue' import 'bootstrap/dist/css/bootstrap.min.css' import ValidateInput, { RulesProp } from './components/ValidateInput.vue' import GlobalHeader, { UserProps } from './components/GlobalHeader.vue' const currentUser: UserProps = {isLogin: true,name: 'Monday' } // 判斷是否是郵箱的格式 const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/export default defineComponent({name: 'App',components: {GlobalHeader,ValidateInput},setup () {const emailRules: RulesProp = [{ type: 'required', message: '電子郵箱不能為空' },{ type: 'email', message: '請輸入正確的電子郵箱格式' }]const emailRef = reactive({val: '',error: false,message: ''})const validateEmail = () => {if (emailRef.val.trim() === '') {emailRef.error = trueemailRef.message = 'can not be empty'} else if (!emailReg.test(emailRef.val)) {emailRef.error = trueemailRef.message = 'should be valid email'}}return {user: currentUser,emailRef,validateEmail,emailRules}} }) </script>

現在,我們在瀏覽器來看下它好不好用。具體效果如下:

大家可以看到,經過抽離后的驗證規則,也正確的顯示了最終的驗證效果。課后呢,大家可以繼續對 RuleProp 的 type 進行擴展,比如多多加一個 range 功能等等。

到了這一步,我們對驗證規則已經進行了簡單的抽離。那接下來要做的事情就是,讓父組件 App.vue 可以獲取到子組件 ValidateInput.vue 中 input 框的值,對其進行數據綁定。

4. v-model

說到 input ,大家首先想到的可能是 v-model 。我們先來看下 vue2 和 vue3 在雙向綁定方面的區別:

<!-- vue2 原生組件 --> <input v-model="val"> <input :value="val" @input="val = $event.target.value"><!-- vue2自定義組件 --> <my-component v-model="val" /> <my-component :value="val" @input="val = argument[0]" /><!-- 非同尋常的表單元素 --> <input type="checkbox" checked="val" @change=""><!-- vue3 compile 以后的結果 --> <my-component v-model="foo" /> h(Comp, {modelValue: foo,'onUpdate: modelValue': value => (foo = value) })

對于 vue2 的雙向綁定來說,主要有以下槽點:

  • 比較繁瑣,需要新建一個 model 屬性;
  • 不管如何,都只能支持一個 v-model ,沒辦法雙向綁定多個值;
  • 寫法比較讓人難以理解。

基于以上 vue2 的幾個槽點,現在我們用 vue3 來對這個組件的 input 值進行綁定,手動對其處理更新和事件發送。

首先我們在子組件 ValidateInput.vue 中進行處理,處理數據更新和事件發送。具體代碼如下:

<template><div class="validate-input-container pb-3"><input type="text"class="form-control":class="{'is-invalid': inputRef.error}":value="inputRef.val"@blur="validateInput"@input="updateValue"><span v-if="inputRef.error" class="invalid-feedback">{{inputRef.message}}</span></div> </template><script lang="ts"> import { defineComponent, reactive, PropType } from 'vue' const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/ interface RuleProp {type: 'required' | 'email';message: string;validator?: () => boolean; }export type RulesProp = RuleProp[] export default defineComponent({name: 'ValidateInput',props: {rules: Array as PropType<RulesProp>,// 創建一個字符串類型的屬性 modelValuemodelValue: String},setup(props, context) {// 輸入框的數據const inputRef = reactive({val: props.modelValue || '',error: false,message: ''})// KeyboardEvent 即鍵盤輸入事件const updateValue = (e: KeyboardEvent) => {const targetValue = (e.target as HTMLInputElement).valueinputRef.val = targetValue// 更新值時需要發送事件 update:modelValuecontext.emit('update:modelValue', targetValue)}const validateInput = () => {if (props.rules) {const allPassed = props.rules.every(rule => {let passed = trueinputRef.message = rule.messageswitch (rule.type) {case 'required':passed = (inputRef.val.trim() !== '')breakcase 'email':passed = emailReg.test(inputRef.val)breakdefault:break}return passed})inputRef.error = !allPassed}}return {inputRef,validateInput,updateValue}} }) </script>

接下來,我們在 App.vue 中對其進行使用,具體代碼如下:

<template><div class="container"><global-header :user="user"></global-header><form action=""><div class="mb-3"><label class="form-label">郵箱地址</label><!-- 此處做修改 --><validate-input :rules="emailRules" v-model="emailVal"></validate-input>{{emailVal}}</div></form></div> </template><script lang="ts"> import { defineComponent, reactive, ref } from 'vue' import 'bootstrap/dist/css/bootstrap.min.css' import ValidateInput, { RulesProp } from './components/ValidateInput.vue' import GlobalHeader, { UserProps } from './components/GlobalHeader.vue' const currentUser: UserProps = {isLogin: true,name: 'Monday' } const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/export default defineComponent({name: 'App',components: {GlobalHeader,ValidateInput},setup () {// 創建emailVal的值const emailVal = ref('monday')const emailRules: RulesProp = [{ type: 'required', message: '電子郵箱不能為空' },{ type: 'email', message: '請輸入正確的電子郵箱格式' }]const emailRef = reactive({val: '',error: false,message: ''})const validateEmail = () => {if (emailRef.val.trim() === '') {emailRef.error = trueemailRef.message = 'can not be empty'} else if (!emailReg.test(emailRef.val)) {emailRef.error = trueemailRef.message = 'should be valid email'}}return {user: currentUser,emailRef,validateEmail,emailRules,emailVal}} }) </script>

現在,我們來看下數據的值是否成功被綁定。具體效果如下:

大家可以看到,數據已經直接的被父組件給獲取到并且也成功的綁定了。

5. 使用$attrs支持默認屬性

上面我們基本上完成了整個組件的基本功能,現在,我們要來給它設置默認屬性,也就是平常我們使用的 placeholder 。如果我們直接在 <validate-input /> 組件中綁定 placeholder ,那么默認地,會直接綁定到它的父組件上面去。因此呢,我們要禁止掉這種行為,讓綁定后的 placeholder 給相應的放置在 input 元素上。

那這一塊內容呢,涉及到的就是 vue3 的 $attrs , $attrs 可以讓組件的根元素不繼承 attribute ,并且可以手動決定這些 attribute 賦予給哪個元素。具體可查看官方文檔:禁用 Attribute 繼承

下面,我們來實現這一塊的功能。

首先是子組件 ValidateInput.vue ,具體代碼如下:

<template><div class="validate-input-container pb-3"><!-- 手動處理更新和發送事件 --><!-- 使用可選 class,用于動態計算類名 --><inputclass="form-control":class="{'is-invalid': inputRef.error}":value="inputRef.val"@blur="validateInput"@input="updateValue"v-bind="$attrs"><span v-if="inputRef.error" class="invalid-feedback">{{inputRef.message}}</span></div> </template><script lang="ts"> import { defineComponent, reactive, PropType } from 'vue' const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/ interface RuleProp {type: 'required' | 'email';message: string;validator?: () => boolean; }export type RulesProp = RuleProp[] export default defineComponent({name: 'ValidateInput',props: {rules: Array as PropType<RulesProp>,modelValue: String},// 如果不希望組件的根元素繼承attribute,那么可以在組件的選項中設置以下屬性inheritAttrs: false,setup(props, context) {// 輸入框的數據const inputRef = reactive({val: props.modelValue || '',error: false,message: ''})// $attrs包裹著傳遞給組件的attribute的鍵值對// console.log(context.attrs)// KeyboardEvent 即鍵盤輸入事件const updateValue = (e: KeyboardEvent) => {const targetValue = (e.target as HTMLInputElement).valueinputRef.val = targetValuecontext.emit('update:modelValue', targetValue)}// 驗證輸入框const validateInput = () => {if (props.rules) {const allPassed = props.rules.every(rule => {let passed = trueinputRef.message = rule.messageswitch (rule.type) {case 'required':passed = (inputRef.val.trim() !== '')breakcase 'email':passed = emailReg.test(inputRef.val)breakdefault:break}return passed})inputRef.error = !allPassed}}return {inputRef,validateInput,updateValue}} }) </script>

之后是父組件 App.vue ,具體代碼如下:

<template><div class="container"><global-header :user="user"></global-header><form action=""><div class="mb-3"><label class="form-label">郵箱地址</label><!-- 需要讓placeholder給添加到子組件的input元素上去,而不是添加到根元素上 --><validate-input:rules="emailRules" v-model="emailVal"placeholder="請輸入郵箱地址"type="text" /></div><div class="mb-3"><label class="form-label">密碼</label><validate-inputtype="password"placeholder="請輸入密碼":rules="passwordRules"v-model="passwordVal" /></div></form></div> </template><script lang="ts"> import { defineComponent, reactive, ref } from 'vue' import 'bootstrap/dist/css/bootstrap.min.css' import ValidateInput, { RulesProp } from './components/ValidateInput.vue' import GlobalHeader, { UserProps } from './components/GlobalHeader.vue' const currentUser: UserProps = {isLogin: true,name: 'Monday' }export default defineComponent({name: 'App',components: {GlobalHeader,ValidateInput},setup () {const emailVal = ref('')const emailRules: RulesProp = [{ type: 'required', message: '電子郵箱不能為空' },{ type: 'email', message: '請輸入正確的電子郵箱格式' }]const passwordVal = ref('')const passwordRules: RulesProp = [{ type: 'required', message: '密碼不能為空' }]return {user: currentUser,emailRules,emailVal,passwordVal,passwordRules}} }) </script>

從上面的代碼中我們可以了解到,通過 inheritAttrs: false 和 $attrs ,實現了我們想要的效果。

我們現在來看下瀏覽器的顯示結果:

💭二、驗證表單ValidateForm

1. 組件需求分析

ValidateInput 除了基本的功能外,還可以進行功能擴散。比如,自定義校驗、更多事件、更多不同的驗證元素。

那么下面,我們要來設計整個驗證表單,也就是 ValidateForm 組件,并且將 ValidateInput 給對應的使用到其中。

我們先來分析下這個 ValidateForm 都有哪些內容。先看下圖:

先看第一部分,我們首先把前面我們封裝的 ValidateInput 給放進去,進行語義化包裹。

第二部分,我們可以對提交的按鈕進行自定義化,比如提交的文字是怎么樣的,提交的按鈕又是怎么樣的。

第三部分,我們需要有一個確定的事件來觸發最后的結果,那么我們就在 ValidateForm 中,獲取最后的結果。

第四部分,算是一個隱藏功能,也是這個組件的一個難點,即獲取每個 ValidateForm 包裹下的 ValidateInput 的驗證結果。

ok,到這里,我們就簡單的對 ValidateForm 進行一個分析,那么下面我們將一步步的來對其進行代碼設計。

2. 使用插槽 slot

首先,我們要先將提交按鈕,做成動態的。一開始初始化一個值,之后呢,可以動態的改變按鈕的文字和事件。那這個要用到的就是 vue 的中具名插槽

我們先在 vue3 項目下的 src|components 定義一個子組件,命名為 ValidateForm.vue 。現在我們來設計它,具體代碼如下:

<template><form class="validate-form-container"><slot name="default"></slot><!-- @click.prevent 用來阻止事件的默認行為 --><!-- 阻止表單提交,僅執行函數submitForm --><div class="submit-area" @click.prevent="submitForm"><slot name="submit"><!-- 給插槽添加一個默認按鈕 --><button type="submit" class="btn btn-primary">提交</button></slot></div></form> </template><script lang="ts"> import { defineComponent, onUnmounted } from 'vue'export default defineComponent({name: 'ValidateForm',components: {},// 在emits字段里面確定所要發送事件的名稱emits: ['form-submit'],setup(props, context) {const submitForm = () => {context.emit('form-submit', true)}return {submitForm}}}) </script>

繼續,我們在 App.vue 中使用子組件 ValidateForm.vue 。具體代碼如下:

<template><div class="container"><global-header :user="user"></global-header><validate-form @form-submit="onFormSubmit"><div class="mb-3"><label class="form-label">郵箱地址</label><!-- 需要讓placeholder和class給添加到input元素上去,而不是添加到根元素上 --><validate-input:rules="emailRules" v-model="emailVal"placeholder="請輸入郵箱地址"type="text" /></div><div class="mb-3"><label class="form-label">密碼</label><validate-inputtype="password"placeholder="請輸入密碼":rules="passwordRules"v-model="passwordVal" /></div><template #submit><span class="btn btn-danger">Submit</span></template></validate-form></div> </template><script lang="ts"> import { defineComponent, reactive, ref } from 'vue' import 'bootstrap/dist/css/bootstrap.min.css' // import ColumnList, { ColumnProps } from './components/ColumnList.vue' import ValidateInput, { RulesProp } from './components/ValidateInput.vue' import ValidateForm from './components/ValidateForm.vue' import GlobalHeader, { UserProps } from './components/GlobalHeader.vue' const currentUser: UserProps = {isLogin: true,name: 'Monday' }export default defineComponent({name: 'App',components: {// ColumnList,GlobalHeader,ValidateInput,ValidateForm},setup () {const emailVal = ref('')const emailRules: RulesProp = [{ type: 'required', message: '電子郵箱不能為空' },{ type: 'email', message: '請輸入正確的電子郵箱格式' }]const passwordVal = ref('')const passwordRules: RulesProp = [{ type: 'required', message: '密碼不能為空' }]// 創建一個函數來監聽結果const onFormSubmit = (result: boolean) => {console.log('1234', result)}return {user: currentUser,emailRules,emailVal,passwordVal,passwordRules,onFormSubmit}} }) </script>

對于以上代碼,我們來做個簡單的分析:

  • 子組件通過 emits 來確定要發送給父組件的事件名稱,之后呢,父組件通過 @事件名稱 的方式來進行調用。
  • 使用具名插槽slot,來對提交表單部分進行動態控制。子組件使用 slot 進行初始化,父組件使用 template 進行動態修改。

3. 父子組件通訊

上面我們解決了第 1 點,組件需求分析中的前三部分。那么現在,我們來看第四點,如何在 ValidateForm 中完成所有 ValidateInput 的驗證。

我們先來完善父組件 ValidateForm.vue 的功能。具體代碼如下:

<template><form class="validate-form-container"><slot name="default"></slot><!-- @click.prevent 用來阻止事件的默認行為 --><!-- 阻止表單提交,僅執行函數submitForm --><div class="submit-area" @click.prevent="submitForm"><slot name="submit"><!-- 給插槽添加一個默認按鈕 --><button type="submit" class="btn btn-primary">提交</button></slot></div></form> </template><script lang="ts"> import { defineComponent, onUnmounted } from 'vue' // 使用 mitt import mitt from 'mitt' type ValidateFunc = () => boolean // 創建一個事件監聽器 export const emitter = mitt()export default defineComponent({name: 'ValidateForm',components: {},// 在emits字段里面確定所要發送事件的名稱// 注意:只能用全部小寫或者駝峰法emits: ['formSubmit'],setup(props, context) {// 用于存放一系列的函數,執行以后可以顯示錯誤的信息let funcArr: ValidateFunc[] = []const submitForm = () => {const result = funcArr.map(func => func()).every(result => result)// 將formSubmit時間進行發送context.emit('formSubmit', result)}// func 即需要接收錯誤信息const callback = (func?: ValidateFunc) => {if (func) {funcArr.push(func)}}// 監聽器就像是一個收音機一樣在等待信息emitter.on('form-item-created', callback)onUnmounted(() => {emitter.off('form-item-created', callback)funcArr = []})return {submitForm}}}) </script>

在上面的代碼中,我們使用 mitt 庫創建了一個事件監聽器 emitter ,供給它的子組件 ValidateInput.vue 使用。同時,創建了一個 formSubmit 事件,用于給它的父組件 App.vue 使用。

接著我們來完善子組件 ValidateInput.vue 的功能。具體代碼如下:

<template><div class="validate-input-container pb-3"><!-- 手動處理更新和發送事件 --><!-- 使用可選 class,用于動態計算類名 --><inputclass="form-control":class="{'is-invalid': inputRef.error}":value="inputRef.val"@blur="validateInput"@input="updateValue"v-bind="$attrs"><span v-if="inputRef.error" class="invalid-feedback">{{inputRef.message}}</span></div> </template><script lang="ts"> import { defineComponent, reactive, PropType, onMounted } from 'vue' import { emitter } from './ValidateForm.vue' const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/ interface RuleProp {type: 'required' | 'email';message: string;validator?: () => boolean; }export type RulesProp = RuleProp[] export default defineComponent({name: 'ValidateInput',props: {rules: Array as PropType<RulesProp>,modelValue: String},inheritAttrs: false,setup(props, context) {const inputRef = reactive({val: props.modelValue || '',error: false,message: ''})const updateValue = (e: KeyboardEvent) => {const targetValue = (e.target as HTMLInputElement).valueinputRef.val = targetValuecontext.emit('update:modelValue', targetValue)}// 驗證輸入框const validateInput = () => {if (props.rules) {const allPassed = props.rules.every(rule => {let passed = trueinputRef.message = rule.messageswitch (rule.type) {case 'required':passed = (inputRef.val.trim() !== '')breakcase 'email':passed = emailReg.test(inputRef.val)breakdefault:break}return passed})inputRef.error = !allPassedreturn allPassed}return true}onMounted(() => {// // 將 input 的值發送出去,即發給給 ValidateForm 組件emitter.emit('form-item-created', validateInput)})return {inputRef,validateInput,updateValue}} }) </script>

有了 emitter 之后, ValidateInput 就在慢慢地把它的消息傳去給它的老父親,也就是 ValidateForm 。

最后,我們在 App.vue 中進行調用。具體代碼如下:

<template><div class="container"><global-header :user="user"></global-header><!-- 將 ValidateForm 中的 formSubmit 事件給傳過來到這里使用 --><validate-form @formSubmit="onFormSubmit"><div class="mb-3"><label class="form-label">郵箱地址</label><validate-input:rules="emailRules" v-model="emailVal"placeholder="請輸入郵箱地址"type="text"ref="inputRef" /></div><div class="mb-3"><label class="form-label">密碼</label><validate-inputtype="password"placeholder="請輸入密碼":rules="passwordRules"v-model="passwordVal" /></div><template #submit><span class="btn btn-danger">Submit</span></template></validate-form></div> </template><script lang="ts"> import { defineComponent, reactive, ref } from 'vue' import 'bootstrap/dist/css/bootstrap.min.css' import ValidateInput, { RulesProp } from './components/ValidateInput.vue' import ValidateForm from './components/ValidateForm.vue' import GlobalHeader, { UserProps } from './components/GlobalHeader.vue' const currentUser: UserProps = {isLogin: true,name: 'Monday' }export default defineComponent({name: 'App',components: {// ColumnList,GlobalHeader,ValidateInput,ValidateForm},setup () {// 用于拿到組件的實例const inputRef = ref<any>()const emailVal = ref('')const emailRules: RulesProp = [{ type: 'required', message: '電子郵箱不能為空' },{ type: 'email', message: '請輸入正確的電子郵箱格式' }]const passwordVal = ref('')const passwordRules: RulesProp = [{ type: 'required', message: '密碼不能為空' }]// 創建一個函數來監聽結果const onFormSubmit = (result: boolean) => {console.log('result', result) // result true}return {user: currentUser,emailRules,emailVal,passwordVal,passwordRules,onFormSubmit,inputRef}} }) </script>

這部分呢,我們成功調用了 formSubmit 事件,并將其進行監聽。

好了,到此,我們的表單驗證組件設計就完成啦!不知道大家是否對這種設計思想有了一個新的認識呢?

👁??🗨?四、結束語

在上面的文章中,我們講到了 Web 世界中的表單元素。從驗證輸入框 ValidateInut 的抽象驗證規則,對 v-model 進行重新設計,以及使用 $attrs 來支持默認屬性。再到 ValidateForm 的使用具名插槽讓提交按鈕高度自定義化,再到最后的 input 之前的父子組件通訊。

整個過程細水長流,但也有很多新的設計思想值得我們去楷模和學習~

到這里,關于本文的講解就結束啦~

如果您覺得這篇文章有幫助到您的的話不妨點贊支持一下喲~~😛

💯 往期推薦

👉前端只是切圖仔?來學學給開發人看的UI設計

👉緊跟月影大佬的步伐,一起來學習如何寫好JS(上)

👉緊跟月影大佬的步伐,一起來學習如何寫好JS(下)

👉組件庫實戰 | 用vue3+ts實現全局Header和列表數據渲染ColumnList

總結

以上是生活随笔為你收集整理的组件库实战 | 教你如何设计Web世界中的表单验证的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。