表單是前端開發中最棘手的部分之一,您可能會在其中發現很多混亂的程式碼。
Vue.js 2之類的基於元件的框架在提高前端程式碼的可伸縮性方面做了很多工作,但是形式問題仍然存在。
在本教程中,我將向您展示新的Vue Composition API(屬於Vue 3)將如何使您的表單程式碼更加整潔和可擴充套件。
為什麼表單程式碼經常很爛像Vue這樣的基於元件的框架的關鍵設計模式是元件組合。該模式告訴我們將應用程式的特性抽象為獨立的、單一用途的元件,這些元件使用屬性和事件來進行狀態通訊。
然而,在這種模式下,表單不能被很好地抽象出來,因為表單的功能和狀態並不明顯屬於任何一個元件,因此將其分離常常會導致與它所解決的問題一樣多的問題。
直到Vue 2為止,Vue都不具備在元件之間重用程式碼的強大方法。這在表單中很重要,因為表單輸入通常明顯不同,但在功能上有許多相似之處。
Vue Composition APIVue 2提供的程式碼重用的主要方法是mixin,許多人認為這是公然的反模式。
Composition API是使用Vue.js定義元件的新方法,它將成為Vue 3的核心功能。它現在也可以在Vue 2中作為外掛使用。
這個新的API旨在解決我提到的一些問題(不僅在形式上,而且在前端應用程式架構的任何方面)。
如果您對Composition API還是不熟悉,或者不清楚它的用途,建議您先閱讀文件,以及我寫的另一篇文章《何時使用新的Vue Composition API(何時不使用)》。
以及我製作的Vue3 Composition API 備忘單,私信回覆:“vue3”獲取
vue3備忘單
Composition API不能替代經典的Vue API,而是可以在需要時使用的東西。正如您將在本文中看到的那樣,建立乾淨且可伸縮的表單程式碼是一個完美的用例。
將Composition API新增到Vue 2專案由於我是在Vue 3釋出之前編寫本教程的,所以讓我們將Composition API作為外掛新增到Vue 2專案中。
$ vue create composition-api-form$ cd composition-api-form$ npm i -S @vue/composition-api接下來,讓我們將外掛新增到main.js中的Vue例項中。
import Vue from "vue";import App from "./App.vue";import VueCompositionApi from "@vue/composition-api";Vue.use(VueCompositionApi);new Vue({ render: h => h(App)}).$mount('#app');建立表單Input元件為了使這個例子簡單,我們將建立一個僅包含兩個輸入的表單——名稱和電子郵件。讓我們將它們建立為自己的獨立元件。
$ touch src/components/InputName.vue$ touch src/components/InputEmail.vue現在讓我們以典型的方式設定InputName元件模板,其中包括帶有 v-model 指令的HTML輸入元素,該模板與該元件建立雙向繫結。
src/components/InputName.vue
<template> <div> <label> Name <input type="text" v-model="input" name="name" /> </label> </div></template><script>export default { name: 'InputName'}</script>設定form讓我們暫時把輸入放在一邊,然後設定表單。您可以將其作為一個單獨的元件來建立,以使其可重用,但是為了本教程的簡單性,我將在App元件模板中宣告它。
我們將新增 novalidate 屬性,以使瀏覽器知道我們將提供自定義驗證。我們還將監聽表單的 submit 事件,阻止其自動提交,並使用稍後將宣告的 onSubmit 方法處理該事件。
然後,我們將新增 InputName 和 InputEmail 元件,並分別將本地狀態值 name 和 email 繫結到它們。
src/App.vue
<template> <div id="app"> <form novalidate @submit.prevent="onSubmit"> <InputName v-model="name" /> <InputEmail v-model="email" /> <button type="submit">Submit</button> </form> </div></template><script>import InputName from "@/components/InputName";import InputEmail from "@/components/InputEmail";export default { name: 'App', components: { InputName, InputEmail }}</script>現在讓我們使用Composition API定義表單功能。我們將向元件定義新增一個 setup 方法,在這個方法中,我們將使用Composition API的 ref 方法宣告兩個狀態變數 name 和 email。這個方法需要從Composition API包中匯入。
然後,我們將宣告一個 onSubmit 函式來處理表單提交。我不會指定任何功能,因為它與本教程無關。
最後,我們需要返回從 setup 函式建立的兩個狀態變數和方法,以便元件的模板可以訪問它們。
src/App.vue
...import { ref } from "@vue/composition-api";export default { name: "App", setup () { const name = ref(""); const email = ref(""); function onSubmit() { // 提交到後端或任何你喜歡的 console.log(name.value, email.value); } return { name, email, onSubmit } }, ...}設定Input接下來,我們將定義 InputName 元件的功能。
由於父表單對這個元件使用的是 v-model,重要的是聲明了一個prop值,該值將是雙向繫結功能實現的一部分(一半)。
讓我們建立一個 setup 函式,將props傳遞到這個方法中,從而使我們能夠訪問元件例項方法。我們可以解構第二個引數並獲得 emit 方法。我們將需要它來完成 v-model 雙向繫結的另一半,也就是說,響應地發出input的新值。
在此之前,讓我們宣告一個狀態變數 input,它將繫結到我們在模板中宣告的input HTML元素。
這個變數的值是我們將要定義的composition function(複合函式) useInputValidator 中返回的值。這個函式將處理所有常見的驗證邏輯。
我們將把值prop傳遞給這個方法,第二個引數是一個回撥函式,它返回經過驗證的輸入值。讓我們使用這個回撥來將這個輸入作為事件發出,並實現 v-model 契約。
import useInputValidator from "@/features/useInputValidator";export default { name: "InputName", props: { value: String }, setup (props, { emit }) { const { input } = useInputValidator( props.value, value => emit("input", value) ); return { input } }}Input驗證器功能現在讓我們建立 useInputValidator 合成函式。為此,我們將首先建立一個 features 資料夾,然後為其建立一個模組檔案。
$ mkdir src/features$ touch src/features/useInputValidator.js在模組檔案中,我們匯出一個函式,我們剛剛看到它將需要兩個引數——從父窗體接收到的 value prop(我們將其稱為 startVal)和將被稱為 onValidate 的回撥方法。
請記住,這個函式需要返回一個 input 狀態變數,因此讓我們來宣告它,分配一個用prop提供的初始值的ref。
在從函式返回 input 值之前,讓我們看一下它的值,並使用input作為引數呼叫 onValidate 回撥。
src/features/useInputValidator.js
import { ref, watch } from "@vue/composition-api";export default function (startVal, onValidate) { let input = ref(startVal); watch(input, value => { onValidate(value); }); return { input }}新增驗證器下一步是新增驗證器功能,對於 InputName 元件,我們只有一個驗證規則——minLength,確保輸入為三個或更多字元。尚未建立的 InputEmail 元件將需要電子郵件驗證。
現在,我們將在src/validateators.js中建立這些驗證器。在實際的專案中,您可能會改用第三方庫。
不會詳細介紹驗證器功能,但需要注意以下兩點:
這些是返回函式的函式。這種結構允許我們通過傳遞成為閉包一部分的引數來定製驗證。每個驗證器返回的函式總是返回一個字串(錯誤訊息),如果沒有錯誤,則返回null。src/validators.js:
const minLength = min => { return input => input.length < min ? `Value must be at least ${min} characters` : null;};const isEmail = () => { const re = /\\S+@\\S+\\.\\S+/; return input => re.test(input) ? null : "Must be a valid email address";}export { minLength, isEmail };回到複合函式中,我們希望元件消費者定義它所需要的驗證,因此,讓我們首先向 validators 中新增另一個引數,該引數應該是驗證函式的陣列。
在input watcher 內部,我們現在將處理驗證功能。讓我們使用驗證器陣列的 map 方法,將輸入的當前值傳遞給每個驗證器方法。
返回值將被捕獲到一個新的狀態變數 error 中,我們還將返回到元件使用方。
src/features/useInputValidator.js:
export default function (startVal, validators, onValidate) { const input = ref(startVal); const errors = ref([]); watch(input, value => { errors.value = validators.map(validator => validator(value)); onValidate(value); }); return { input, errors }}最後回到 InputName 元件,我們現在將為 useInputValidator 方法提供所需的三個引數。記住,第二個引數現在是一個驗證器陣列,所以讓我們就地宣告一個數組,並傳入 minLength,我們將從驗證器檔案中匯入該程式碼。
minLength 是一個工廠函式,因此我們呼叫傳遞我們想要指定的最小長度的函式。
現在,我們還從合成函式返回了兩個物件——input和error。兩者都將從 setup 方法返回,以在元件的渲染上下文中提供可用性。
src/components/InputName.vue
...import { minLength } from "@/validators";export default { ... setup (props, { emit }) { const { input, errors } = useInputValidator( props.value, [ minLength(3) ], value => emit("input", value) ); return { input, errors } }}顯示錯誤這是我們將新增到此元件的最後一個功能。不過,在繼續之前,請花點時間了解一下此程式碼比我們使用mixin所看到的程式碼更具可讀性,這一點很重要。
一方面,我們清楚地看到了狀態變數的宣告和修改位置,而無需瀏覽到單獨的mixin模組檔案。另一方面,我們不必擔心區域性變數和組合函式之間的名稱衝突。
轉到 InputName 元件模板,現在可以顯示一系列潛在的錯誤。讓我們將其委託給一個名為 ErrorDisplay 的顯示元件。
<template> <div> <label> Name <input type="text" v-model="input" name="name" /> </label> <ErrorDisplay :errors="errors" /> </div></template><script>...import ErrorDisplay from "@/components/ErrorDisplay";export default: { ... components: { ErrorDisplay }}</script>ErrorDisplay 的功能太簡單了,這裡就不顯示了。
重用程式碼這就是我們基於Composition API 表單的基本功能。本教程的目標是建立乾淨的、可伸縮的表單程式碼,我想通過定義我們的第二個自定義輸入 InputEmail 向您證明這一點。
如果您達到了本教程的目標,那麼在沒有我的說明情況下,您可以毫無困難地理解它!
src/components/InputEmail
<template> <div> <label> Email <input type="email" v-model="input" name="email" /> </label> <ErrorDisplay v-if="input" :errors="errors" /> </div></template><script>import useInputValidator from "@/features/useInputValidator";import { isEmail } from "@/validators";import ErrorDisplay from "./ErrorDisplay";export default { name: "InputEmail", props: { value: String }, setup (props, { emit }) { const { input, errors } = useInputValidator( props.value, [ isEmail() ], value => emit("input", value) ); return { input, errors } }, components: { ErrorDisplay }}</script>完整原始碼,請私信回覆:“vue3-form”
本文完全原始碼
作者簡介:Web前端工程師,全棧開發工程師、持續學習者。
#Vue.js#