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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > HTML >内容正文

HTML

获取form表单_【第1535期】前端 Form 的表单的一个通用解决方案

發(fā)布時(shí)間:2025/3/8 HTML 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 获取form表单_【第1535期】前端 Form 的表单的一个通用解决方案 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

今日早讀文章由阿里@布達(dá)投稿分享。

@布達(dá),Alibaba Fusion項(xiàng)目組的?;麧幜?。主要專注在設(shè)計(jì)系統(tǒng)、組件、可視化搭建這個(gè)領(lǐng)域

正文從這開(kāi)始~~

Fusion Next - Form 表單解決方案

前端的Form 表單主要用于解決數(shù)據(jù)獲取、數(shù)據(jù)校驗(yàn)、數(shù)據(jù)賦值 這三大類問(wèn)題。這篇文章里面的提供的解決方案能夠比較完美的用在 React 框架上,但是解決問(wèn)題的思路相信應(yīng)該是可以使用于任何框架語(yǔ)言。

中后臺(tái)的表單組件已經(jīng)不僅僅有 input 和 select,可能還擴(kuò)展到 范圍選擇器、日期選擇器 等,這些組件往往為了實(shí)現(xiàn)更優(yōu)雅的UI和更使用的交互會(huì)在原生的組件上面做多層封裝,而經(jīng)過(guò)多層疊加后可能已經(jīng)看不到原生表單元素的影子了。比如經(jīng)過(guò)封裝下面這段 DOM 結(jié)構(gòu)經(jīng)過(guò)樣式修改也可能成為一個(gè)輸入組件,雖然完全看不到 input 的影子

contentEditable>

所以為了便于大家理解我這里從傳統(tǒng)的原生 form 說(shuō)起,好讓大家有一個(gè)遞進(jìn)的過(guò)程。

引子:原生 form 表單

最初始的一份代碼如下,代碼很簡(jiǎn)單,看著也很舒服。

action="/api/post" method="post">
?username: name="username" />
?password: name="password" /> type="submit">submit

但是你開(kāi)始做數(shù)據(jù)校驗(yàn)相關(guān),表單就立刻變得復(fù)雜多了。如下:代碼增多了一倍。

function checkname(target) {const value = target.value;if (value.length < 10) {
? ? ?document.getElementById('username_msg').innerHTML = '長(zhǎng)度必須>10'} else {
? ? ?document.getElementById('username_msg').innerHTML = ''}}function checkpassword(target) {const value = target.value;if (!value.match(/^[\w]{6,16}$/)) {
? ? ?document.getElementById('password_msg').innerHTML = '密碼必須 6-16 位字母數(shù)字'} else {
? ? ?document.getElementById('password_msg').innerHTML = ''}}function getInitData() {
? ?ajax({
? ? ?url:'/api/data',
? ? ?success:function(data) {
? ? ? ?document.getElementById('username') = data.username;});}
?getInitData(); action="/api/post" method="post">
username: name="username" onchange="checkname(this)"/> id="username_msg">
passowrd: name="password" onchange="checkpassword(this)"/> id="password_msg"> type="submit">submit

如果把DOM的部分也用JS來(lái)實(shí)現(xiàn),基本可以做到只修改JS不需要再動(dòng)DOM結(jié)構(gòu),但是也讓JS的復(fù)雜度增高不少。

React 里面所有的DOM結(jié)構(gòu)都是自己通過(guò)JS 生成的,JSX也可以方便的實(shí)現(xiàn)DOM結(jié)構(gòu)。但這里我拿原生表單舉例,只是想說(shuō)用 React 寫出來(lái)的原生表單,并不比用原生 JS 的優(yōu)雅多少!!!

React 中的原生 form 表單

同樣一段最簡(jiǎn)單的功能,套在 react 框架下面是這個(gè)樣子。

class Demo extends React.Component {
?render() {return <form action="/api/post" method="post">
? ? ?username: <input name="username" />
? ? ?passowrd: <input name="password" /><button type="submit">submitbutton>form>}}

比如同樣想要實(shí)現(xiàn)校驗(yàn)輸入自動(dòng)校驗(yàn) 和 賦值,看下面一段代碼,想想就是一大堆事情要做。

class Demo extends React.Component {
?state = {
? ?username: '',
? ?password: '',
? ?usernameMsg: '',
? ?passwordMsg: '',};
?checkname = e => {// 獲取數(shù)據(jù)const value = e.target.value;// 受控模式賦值this.setState({
? ? ?username: value,});// 校驗(yàn)數(shù)據(jù)if (value.length < 10) {this.setState({
? ? ? ?usernameMsg: '長(zhǎng)度必須>10',});} else {this.setState({
? ? ? ?usernameMsg: '',});}};
?checkpassword = e => {// 獲取數(shù)據(jù)const value = e.target.value;// 受控模式賦值this.setState({
? ? ?password: value,});// 校驗(yàn)數(shù)據(jù)if (!value.match(/^[\w]{6,16}$/)) {this.setState({
? ? ? ?passwordMsg: '密碼必須 6-16 位字母數(shù)字',});} else {this.setState({
? ? ? ?passwordMsg: '',});}};
?handleSubmit = () => {
? ?ajax({
? ? ?url: '/api/post',
? ? ?data: {
? ? ? ?username: this.state.username,
? ? ? ?password: this.state.password,},
? ? ?success: () => { // success},});};
?render() {// 獲取數(shù)據(jù)和錯(cuò)誤信息const { username, password, usernameMsg, passwordMsg } = this.state;return (<form action="/api/post" method="post">
? ? ? ?username: <input value={username} onChange={this.checkname} />{usernameMsg}span>
? ? ? ?passowrd: <input value={password} onChange={this.checkpassword} />{passwordMsg}span><button type="submit" onClick={this.handleSubmit}>
? ? ? ? ?submit ? ? ? ?button>form>);}}

代碼有點(diǎn)長(zhǎng),大家大致看一眼干什么的就行,從上面一系列代碼中基本可以總結(jié)出一個(gè)現(xiàn)象,要想實(shí)現(xiàn)表單數(shù)據(jù)獲取、校驗(yàn),基本離不開(kāi) onChange 這個(gè)方法,而且是有幾個(gè)表單控件,就要寫幾個(gè) onChange 。(以上代碼可直接運(yùn)行,可以在?https://codepen.io/frankqian/pen/XOROBw?editors=0010?調(diào)試)

其實(shí)這里和框架并沒(méi)有什么關(guān)系,因?yàn)椴还苡檬裁纯蚣芤胱龅?賦值和校驗(yàn) 這兩個(gè)功能,基本一定要在 input 上面綁定 onChange。 所以如果有個(gè)通用的工具可以自動(dòng)幫你把這些onChange的綁定都做了,再把校驗(yàn)規(guī)則固定下,是不是所有的表單問(wèn)題都可以解決了呢?是的通用表單解決方案就是按照這種思路設(shè)計(jì)出來(lái)的!

適用于所有 React 表單組件的解決方案

所有的用 React 寫成的組件都可以使用該方案。甚至 非 React 體系也可以使用改思路來(lái)解決問(wèn)題。

基于所有表單控件都需要綁定 onChange 做數(shù)據(jù)獲取和校驗(yàn)的原則,所以我設(shè)計(jì)了一個(gè) Field 工具。這個(gè)工具原理很簡(jiǎn)單,就是可以自動(dòng)幫你綁定 value + onChange 解決上面一長(zhǎng)串代碼的問(wèn)題。

const field = new Field(this);
field.init('username');

field.init 會(huì)自動(dòng)返回 value + onChange ,內(nèi)容如下:

{
?value: "",
?onChange: ? ()}

下面這張圖簡(jiǎn)單表面 Field 和 React 體系之間的關(guān)系。

1. 使用 Field 獲取數(shù)據(jù)
import {Field} from '@alifd/next';class Demo extends React.Component {
?field = new Field(this);
?handleSubmit = () => {
? ?console.log(this.field.getValues()); // 獲取數(shù)據(jù)}
?render() {const {init} = this.field;return
? ? ?username: <input {...init('username')} />
? ? ?passowrd: <input {...init('password')} /><button onClick={this.handleSubmit} >submitbutton>form>}}

這樣一個(gè)表單的數(shù)據(jù)獲取問(wèn)題就解決了,代碼簡(jiǎn)潔了很多。 Demo 在這里?https://codepen.io/frankqian/pen/xMdoxZ?editors=0010?可以自己調(diào)試

2. 表單校驗(yàn)

既然能夠獲取到數(shù)據(jù)了,那邊表單校驗(yàn)是順手的事情,因?yàn)樾r?yàn)只依賴數(shù)據(jù)。我們只需要對(duì)集中固定的交互性形式和校驗(yàn)規(guī)則做抽象就好了。

交互形式上大概包含以下三類

  • 輸入的時(shí)候?qū)崟r(shí)校驗(yàn),一般 onChange 觸發(fā)

  • 離開(kāi)焦點(diǎn)的時(shí)候校驗(yàn),一般 onBlur 觸發(fā)

  • 通過(guò)自定義的操作來(lái)觸發(fā)校驗(yàn),自己調(diào)用 api 觸發(fā)

常見(jiàn)的校驗(yàn)規(guī)則抽象
規(guī)則名稱描述類型觸發(fā)條件/數(shù)據(jù)類型
required不能為空Booleanundefined/null/“”/[]
pattern校驗(yàn)正則表達(dá)式正則
minLength字符串最小長(zhǎng)度 / 數(shù)組最小個(gè)數(shù)NumberString/Number/Array
maxLength字符串最大長(zhǎng)度 / 數(shù)組最大個(gè)數(shù)NumberString/Number/Array
length字符串精確長(zhǎng)度 / 數(shù)組精確個(gè)數(shù)NumberString/Number/Array
min最小值NumberString/Number
max最大值 / 數(shù)組精確個(gè)數(shù)NumberString/Number
format對(duì)常用 pattern 的總結(jié)url/email/tel/numberString
validator自定義校驗(yàn)Function

這里說(shuō)明下表單是弱類型的數(shù)據(jù)。比如 input 框里面你希望用戶輸入的是整數(shù),返回的 value 類型可能有兩種

  • “123456”, String 類型的整數(shù)校驗(yàn)方式為 :/\d+/

  • 123456, Number 類型的整數(shù)校驗(yàn)方式為: typeof Value === ‘number’

這個(gè)時(shí)候要求用戶一定要返回 Number 類型才能校驗(yàn)非常不友好,所以在 Field 校驗(yàn)邏輯里面就把類型的問(wèn)題處理掉了,而不是交給用戶去判斷。

上面是小插曲,我們繼續(xù)看如下 Field + 表單的代碼,解決了數(shù)據(jù)獲取、表單校驗(yàn)的所有功能。

import { Field } from '@alifd/next';class Demo extends React.Component {
?field = new Field(this);
?handleSubmit = (e) => {
? ?e.preventDefault();this.field.validate(); // 自定義校驗(yàn)
? ?console.log(this.field.getValues()); // 獲取數(shù)據(jù)}
?render() {const {init, getError} = this.field;return
? ? ?username: <input {...init('username', {rules: { required: true, minLength: 10}})} /><span style={{color: 'red'}}>{getError('username')}</span> ?{/**錯(cuò)誤信息**/}
? ? ?passowrd: <input {...init('password', {rules: {
? ? ? ? ?pattern: /^[\w]{6,16}$/,
? ? ? ? ?message: '密碼必須 6-16 位字母數(shù)字'}})} /><span style={{color: 'red'}}>{getError('password')}</span> ?{/**錯(cuò)誤信息**/}<button onClick={this.handleSubmit} >validatebutton>form>}}

這樣之前可能需要 70 行的代碼 24 行就可以解決了,可以讓代碼清晰不少。調(diào)試demo見(jiàn):?https://codepen.io/frankqian/pen/vbZmXE?editors=0010

3. 寫自定義的事件

既然 init 會(huì)自動(dòng)返回 onChange,那么如果我希望自己在 onChange 里面加一些邏輯改怎么處理呢。

如果直接寫 onChange 會(huì)被 init 覆蓋掉,不管寫前面還是后面總有一個(gè)onChange會(huì)被覆蓋掉

onChange={(value) => console.log(value)}
?{...init('username')} />

所以在 init 提供了 props 可以把組件原生的 props 透?jìng)鬟M(jìn)去,Field內(nèi)部會(huì)做好 hook 邏輯處理。

{...init('username', {props: {onChange:(value) => console.log(value)}
})} />

大家用起來(lái)覺(jué)得很麻煩,后面會(huì)再介紹如果在 Form 層面設(shè)計(jì)的更加趨向于原生方法的使用

4. 自己寫的表單組件怎么用

現(xiàn)在很多React 組件是在原生組件之上又做了封裝,還有很多組件可能并沒(méi)有包裹表單元素(比如 Fusion Select 里面并沒(méi)有 select 元素,下拉框是自己做的 )。但是只要你自己寫的組件也遵循表單的規(guī)則就可以使用該方案。

4.1 基本: value + onChange 受控規(guī)則

這個(gè)規(guī)則其實(shí)來(lái)自原生 html 的組件,我們自己寫的組件只要按照標(biāo)準(zhǔn)來(lái)都可以使用 Field。

自己寫的組件比起原生的表單組件會(huì)更加美觀,交互更友好。只要遵循規(guī)范都能在 field 里面使用,詳細(xì)demo 見(jiàn)?https://codepen.io/frankqian/pen/gqRWJx?editors=0010這個(gè)規(guī)則其實(shí)來(lái)自原生?html 的組件,我們自己寫的組件只要按照標(biāo)準(zhǔn)來(lái)都可以使用 Field。

4.2 高級(jí)功能:滿足更加人性化的需求

還有一些其他更加細(xì)粒度的規(guī)則,是為了讓你的組件更加好的適配高級(jí)功能,比如:

一鍵 reset 清空所有數(shù)據(jù)。因?yàn)槊總€(gè)組件的接收數(shù)據(jù)類型不一樣,所以統(tǒng)一為在 willReceiveProps 里面接收 value=undefined

componentWillReceiveProps(nextProps) {if ('value' in nextProps ) {this.setState({
? ? ? ? ? value: nextProps.value === undefined? []: nextProps.value ? // ?設(shè)置組件的被清空后的數(shù)值})}}

一次交互操作只拋一次 onChange

比如 upload 上傳,如果一次上傳觸發(fā)上百次 onChange,那么整個(gè)頁(yè)面會(huì)跟著一起 Render 幾百次,非常影響性能

Upload 上傳完成才是用戶想要的結(jié)果

比如 Slider, 在拖動(dòng)的時(shí)候如果實(shí)時(shí)觸發(fā) onChange,那么在拖動(dòng)滑塊的時(shí)候可能會(huì)非??D。所以鼠標(biāo)松開(kāi)的那個(gè)瞬間觸發(fā)才是比較合理的操作,其他的拖拽事件可以交給 onProgress

Slider 的拖動(dòng)釋放的那一刻可能才是用戶想要的數(shù)據(jù)

Fusion Next 的表單組件基本都已經(jīng)是按照這套規(guī)范標(biāo)準(zhǔn)實(shí)現(xiàn)了,詳細(xì)可以查看這里的文檔?https://fusion.design/component/field?拉到最下面

Form 組件讓體驗(yàn)持續(xù)升級(jí)

上面知道了 Field 可以解決校驗(yàn)、獲取、賦值等數(shù)據(jù)方面的問(wèn)題,但是并不能解決 UI 和 交互的問(wèn)題,在布局和錯(cuò)誤展示的時(shí)候需要自己來(lái)控制。

常用場(chǎng)景抽象讓布局更輕松

場(chǎng)景的布局有水平 inline 布局、垂直的分欄布局,通過(guò) FormItem 的 api 可以非常輕松的做到。

垂直布局

label="Username:"> name="first" ?placeholder="first"/> name="second" placeholder="second"/> label="Password:" required> htmlType="password" name="pass" placeholder="Please enter your password!"/> label=" ">Submit垂直布局

水平布局

inline>...水平布局

標(biāo)簽內(nèi)置

labelAligin="inset">...標(biāo)簽內(nèi)嵌
  • 輔助錯(cuò)誤展示

  • 出錯(cuò)的時(shí)候自動(dòng)展示錯(cuò)誤信息,不需要自己 getError 判斷。 每種狀態(tài)怎么展現(xiàn)由各自的組件自己實(shí)現(xiàn)。減少和Form的耦合

    每個(gè)組件的加載中、成功、失敗,都由組件自己實(shí)現(xiàn),Form 只是在校驗(yàn)的時(shí)候傳遞 state 給各個(gè)組件,這樣不需要 Form 去關(guān)心每個(gè)組件應(yīng)該展現(xiàn)為什么樣!

    state="error" /> ?// 錯(cuò)誤狀態(tài) state="loading" /> // 加載中 state="success" /> // 成功 state="error" /> // 錯(cuò)誤狀態(tài)

    進(jìn)一步優(yōu)化 Form 讓使用更簡(jiǎn)單

    以上我們還是 Field + Form 配合來(lái)使用的,代碼基本是這個(gè)樣子。

    import { Form, Input, Field, Button } from '@alifd/next';const FormItem = Form.Item;class Demo extends React.Component {
    ?field = new Field(this);
    ?handleSubmit = () => {this.field.validate();}
    ?render() {const {init} = this.field;return ?<Form field={this.field}><FormItem label="Username:"><Input {...init('username', {
    ? ? ? ? ? ? ?rules: {required}})} />FormItem><FormItem label="Password:"><Input {...init('password', {
    ? ? ? ? ? ? ?rules: {pattern:/[\w]{6,16}/}})} htmlType="password" />FormItem><FormItem label=" "><Button onClick={this.handleSubmit} >SubmitButton>FormItem>Form>}}

    可能寫多了之后就會(huì)想,每個(gè)組件都要使用 init 、都需要寫 rules 規(guī)則,而且在 jsx 中寫一大串的 JSON 數(shù)據(jù)。

    是否有方法讓數(shù)據(jù)獲取和校驗(yàn)變得更簡(jiǎn)單,讓代碼再進(jìn)一步的簡(jiǎn)化呢?

    進(jìn)一步集成 Field 能力而弱化用法

    針對(duì)以上問(wèn)題對(duì) Form 進(jìn)一步優(yōu)化,把 Field 的能力整合進(jìn)了 Form,而把 Field 的用法進(jìn)一步弱化,讓大家不需要再關(guān)心 init/取數(shù)據(jù) 等問(wèn)題。代碼如下:

    import { Form, Input, Button } from '@alifd/next';const FormItem = Form.Item;class Demo extends React.Component {
    ?handleSubmit = (values, errors) => {if (errors) {// 校驗(yàn)出錯(cuò) return;}
    ? ?console.log(values) // 獲取數(shù)據(jù)}
    ?render() {return ?<Form><FormItem label="Username:" required><Input name"username" />FormItem><FormItem label="Password:" pattern={/[\w]{6,16}/}><Input name="password" htmlType="password" />FormItem><FormItem label=" "><Form.Submit validate onClick={this.handleSubmit} >SubmitForm.Submit>FormItem>Form>}}

    上面代碼中可以看出幾個(gè)優(yōu)化點(diǎn):

    • 不需要關(guān)注 Field 用法,改成 Form API 的方式。用法簡(jiǎn)單直接不少

    • 通過(guò) name 來(lái)進(jìn)行數(shù)據(jù)初始化,也更加接近原生 form 的用法,大家更容易理解。

    • 校驗(yàn)功能 API 化,代碼更加簡(jiǎn)潔,可讀性增強(qiáng)

    后記

    Form 的優(yōu)化一定不會(huì)僅僅止于此,因?yàn)樵趯?shí)際業(yè)務(wù)中會(huì)遇到更加復(fù)雜的功能。

    很多業(yè)務(wù)為了更加方便快捷,會(huì)抽象常用的組件布局,通過(guò)后端接口吐出JSON schema的方式直接在前端動(dòng)態(tài)展示表單,雖然比較業(yè)務(wù)化當(dāng)時(shí)確實(shí)方便快捷,能夠極大的解決效率問(wèn)題;

    又或者把常用的表單類場(chǎng)景做成業(yè)務(wù)組件、模塊模板,在使用的時(shí)候直接下載使用。比如:Fusion的表單類模塊:https://fusion.design/module?category=表單

    方案很多,總有適合自己的一套。

    相關(guān)鏈接

    • field 組件 demo: https://fusion.design/component/field

    • form 組件 demo: https://fusion.design/component/form

    • Fusion Next 組件倉(cāng)庫(kù): https://github.com/alibaba-fusion/next

    關(guān)于本文
    作者:@布達(dá)
    原文:https://www.yuque.com/docs/share/8238bd4e-cfb2-42de-afa7-bc2b2f2ab0dc

    最后,為你推薦

    【第998期】JSON schema與表單驗(yàn)證

    【第937期】探索兩種優(yōu)雅的表單驗(yàn)證——策略設(shè)計(jì)模式和ES6的Proxy代理模式

    總結(jié)

    以上是生活随笔為你收集整理的获取form表单_【第1535期】前端 Form 的表单的一个通用解决方案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。