白嫩娇妻被交换经过_被弄出白浆喷水了视频_亚洲依依成人_小蝌蚪视频污下载

當(dāng)前位置: 首頁(yè) / 技術(shù)干貨 / 正文
react 表單方案一覽

2023-02-02

   表單

  在 React 中裸用表單需要維護(hù)大量的 value 和 onChange,自然需要選擇合適的表單方案。那么在做技術(shù)選型前,不妨先列出我們對(duì)于react 表單方案的期望。

  基礎(chǔ)功能

  這些功能點(diǎn)是每個(gè)表單方案必須擁有的,也都比較基礎(chǔ),市面上大部分表單方案 star 多的少的、自己造輪子的 也都會(huì)囊括這些功能。

  ?? 收集表單數(shù)據(jù)

  ?? 管理表單狀態(tài)(未驗(yàn)證、未提交、校驗(yàn)狀態(tài)等)

  ?? 支持表單校驗(yàn)

  ?? 支持自定義觸發(fā)校驗(yàn)時(shí)機(jī)(submit/hover/實(shí)時(shí)/自定義等)

  ?? 支持自定義校驗(yàn)錯(cuò)誤后的信息展示

  ?? 支持自定義組件/接入第三方UI庫(kù)

  羅列表單方案

  通過搜索引擎找出 比較常用的 / 成熟的 / 熱門的 表單方案:

  · Antd Form

  · Fusion Next Form

  · formik

  · react-final-form

  · NoForm

  · uform

  · redux-form

  · react-jsonschema-form

  · Informed

  · formal

  這么多表單方案,該如何做選擇?我們先大致過一遍,redux-form 依賴 redux,dan 都說(shuō) You Might Not Need Redux,從耦合性的角度考慮表單方案不應(yīng)該依賴 redux ,同理 Fusion Next Form 是 fusion 內(nèi)建的表單方案,react-jsonschema-form 強(qiáng)依賴了 Bootstrap,暫不考慮。Antd Form 基于的 rc-form 可以脫離 Antd 使用。

  hooks 形式的有 Informed、formal、formik@v2.x、react-final-form-hooks。對(duì)于新的一些技術(shù)我更看重他的使用場(chǎng)景,使用新技術(shù)會(huì)帶來(lái)什么好處?曾經(jīng)我也激進(jìn)過,一味求新,現(xiàn)在更多的是把技術(shù)當(dāng)作實(shí)現(xiàn)產(chǎn)品的工具。目前來(lái)看這些并沒有帶來(lái)特別明顯的優(yōu)勢(shì),反倒是需要承擔(dān)“小白鼠”的角色,所以暫且先觀望看看。

  關(guān)注點(diǎn)

  初篩完一輪后,詳細(xì)看了文檔,整理出了如下一些關(guān)注點(diǎn),希望通過這些功能點(diǎn)對(duì)這些方案做一次橫向的對(duì)比。

  表單的描述形式

  既然是 react 的表單方案,大部分都是基于 JSX 的。表單的最上層大同小異,無(wú)非會(huì)有個(gè) Form 或是自己的 Class,在這里不做討論,更多討論的是表單項(xiàng)的代碼書寫方式。

  JSX + JSON

  第一類的代表是 rc-form、formal,在 JSX 里寫一個(gè) JSON 描述校驗(yàn)規(guī)則,將和表單項(xiàng)有關(guān)的信息(字段名,校驗(yàn)規(guī)則等)都集中在一處描述,通過展開運(yùn)算符向 UI 組件傳入“處理好的”props,自動(dòng)綁定 value、onChange。 這個(gè)應(yīng)該是最常用的,我的感受是表單一旦多起來(lái)或是代碼寫多了,會(huì)占用大量篇幅,滿屏幕的 JSON 可能會(huì)有點(diǎn)視覺疲勞。

  // createForm()(Component)

  render() {

  const { getFieldProps } = this.props.form;

  return (

  <input {...getFieldProps('name', {

  rules: {

  required: true,

  message: 'Please input your name!'

  }

  })}/>

  );

  }

  表單元素抽象概念

  第二大類則是有 Field 或是 FormItem (之后簡(jiǎn)稱為 F)的表單元素概念,這樣的設(shè)計(jì)我更加看好,將 JSON 的寫法改成了正常的 JSX,整體感官上舒服了不少,也有比較多的庫(kù)都實(shí)現(xiàn)了類似的 API。F 作為表單元素的各種抽象,對(duì)外提供一致接口,例如字段名、校驗(yàn)規(guī)則、表單域組件、value 等等。

<F type="email" name="email" placeholder="Email" />
<F component="select" name="color">
    <option value="red">Red</option>
  <option value="green">Green</option>
  <option value="blue">Blue</option>
</F>
<F name="firstName" component={CustomInputComponent} />
<F name="age">
    {({ input, meta }) => (
        <div>
            <label>Age</label>
            <input {...input} type="text" placeholder="Age" />
            {meta.error && meta.touched && <span>{meta.error}</span>}
        </div>
    )}
</F>

  那么表單元素 F 包含了什么?表單標(biāo)簽?表單域?錯(cuò)誤提示?每個(gè)庫(kù)對(duì)此有不同的理解。

  formik

  個(gè)人理解 formik 更傾向于 F 是一個(gè)純粹的表單域(Input, Select ...),F(xiàn)ield 的 component 字段默認(rèn)就是 input,認(rèn)為“完整”的 F 是 Fieldset,這個(gè)在 demo 中有體現(xiàn),當(dāng)然 API 也并沒有限制開發(fā)者自由發(fā)揮,F(xiàn)ield 支持 render props。

  react-final-form

  react-final-form 的 F 設(shè)計(jì)的比較開放,并沒有官方的說(shuō)法,一千個(gè)前端就有一千個(gè)哈姆雷特,F(xiàn) 是什么由開發(fā)者定義,提供了三個(gè)字段 component, render, children 供選擇。

  uform

  uform 的 F 是什么由開發(fā)者定義,API 設(shè)計(jì)得和前兩者不太一樣,F(xiàn) 默認(rèn)是一個(gè)表單域,開發(fā)者可以通過 registerFieldMiddleware 這個(gè) API 設(shè)置 F 的 Wrapper。

  NoForm

  NoForm 對(duì)于 F 有自己的理解,認(rèn)為 F 是一個(gè)完整的表象元素抽象,在文檔中有詳細(xì)描述。他給我的感受和前幾個(gè)不太一樣,他為開發(fā)者設(shè)計(jì)好了表單的一切,試圖給出一個(gè)最佳實(shí)踐,什么元素(表單標(biāo)簽、錯(cuò)誤提示、表單域前綴、后綴等)應(yīng)該放在哪里,對(duì)應(yīng)的你需要傳入哪個(gè)參數(shù)都幫你決策好了,當(dāng)然這樣的設(shè)計(jì)也犧牲掉了一部分靈活性,使用起來(lái)更像一個(gè)表單域的 Wrapper。

<F label="input" name="input">
  <Input />
</F>

  UI 組件適配

  現(xiàn)在前端開發(fā)項(xiàng)目已經(jīng)離不開 UI 組件庫(kù)了,作為表單方案,如何接入組件庫(kù)也是一個(gè)關(guān)注點(diǎn)。

  rc-form、formal

  通過特定的 API 結(jié)合 Spread syntax 往 UI 組件傳遞參數(shù),主要是 value 和 onChange,不需要專門編寫對(duì)應(yīng)的適配 UI 組件,能夠快速接入。

import { Input } from 'antd';

{getFieldDecorator('name', {
    rules: [{ required: true }],
  //valuePropName: 'checked',  // <Switch/>
})(
    <Input />
)}

  表單元素概念的設(shè)計(jì)通常還會(huì)有一層 “適配層” 亦或是 “接入層”,類似 Adapter / Wrapper 的概念,用于更快速的接入第三方 UI 組件庫(kù)。上層的 F 負(fù)責(zé)維護(hù) value,error、onChange 等等,適配層根據(jù)下層 UI 組件的要求傳遞這些屬性,下層的 UI 組件負(fù)責(zé)純展示。各個(gè)庫(kù)對(duì)適配層的設(shè)計(jì)也不盡相同。

  formik

  formik 的 Field 會(huì)傳遞兩個(gè)特定的參數(shù),分別是 field 和 form,前者主要包含 value, onChange, onBlur,后者包含 isSubmitting, touched, errors 等一些表單狀態(tài)及工具方法。因?yàn)榇蟛糠?UI 組件接收的是 value 和 onChange,所以需要專門編寫對(duì)應(yīng)的適配層。

import { Input } from 'antd';

const InputWrapper = ({ field: {name}, form: { touched, errors }, ...restProps }) => (
  <div>
        <Input {...field} {...restProps}/>
        {touched[field.name] && 
      errors[field.name] && <div className="error">{errors[field.name]}</div>}
    </div>
)

  react-final-form

  react-final-form 和 formik 在 F 設(shè)計(jì)上雷同,提供了 input 和 meta,分別對(duì)應(yīng) formik 的 field 和 form,在字段上有些許差異,就不做多余的描述了,見官方demo:

  https://codesandbox.io/s/40mr0v2r87

  https://codesandbox.io/s/9ywq085k9w

  uform

  uform 提供了 registerFormField 用于注冊(cè)表單字段組件,registerFieldMiddleware 用于設(shè)置 wrapper。當(dāng)然作為阿里內(nèi)部的表單框架,自帶了自家的兩個(gè)UI庫(kù)適配層 @uform/antd, @uform/next,還是深度定制的。

  import { Input } from 'antd'

  // 最簡(jiǎn)版

  registerFormField('testInput', Input)

  registerFieldMiddleware(Field => {

  return props => {

  const { errors, schema } = props

  // errors handle todo

  return React.createElement(

  'div',

  {},

  React.createElement(span, {}, schema.title),

  React.createElement(Field, props)

  )

  }

  })

  <Field type="testInput" label="name" />// 應(yīng)用

  值得一提的是這個(gè) string type 的設(shè)計(jì),寫表單的時(shí)候不需要從外部 import 組件,預(yù)先注冊(cè)好之后,F(xiàn)ield 組件就會(huì)幫助開發(fā)者匹配相對(duì)應(yīng)的組件,是一個(gè)能夠提升工作幸福感的設(shè)計(jì)。

  NoForm

  如上文提到 NoForm 的 FormItem 本身更像是個(gè)表單域的 Wrapper,而且 FormItem 在內(nèi)部會(huì)做一個(gè) cloneElement,將處理好的 props 傳遞給子組件,因此接入 UI 庫(kù)理論上甚至不需要編寫額外的適配層。但是出于對(duì)外提供一致接口的考慮,比如 Switch 的值是 checked,Input 是 value,還是需要有這個(gè) UI 適配層。當(dāng)然同作為阿里內(nèi)部的框架,也提供了兩個(gè)組件庫(kù)的適配層 nowrapper。

import { Input } from 'antd'

<FormItem label="input" name="input">
  <Input />
</FormItem>

  表單校驗(yàn)

  表單校驗(yàn)是每個(gè)表單方案繞不過的一道坎,也是我們的重點(diǎn)關(guān)注點(diǎn)之一。通常會(huì)支持表單級(jí)、字段級(jí)的驗(yàn)證,常規(guī)功能在這里就不討論了,歸納總結(jié)了一些看到的特點(diǎn):

  校驗(yàn)規(guī)則外置

  通常的校驗(yàn)規(guī)則是與 F 一一對(duì)應(yīng),在 F 相關(guān)的 JSX 中描述,每個(gè) F 上會(huì)有類似 validate 的字段 或是 通過展開運(yùn)算符傳入,代表是 react-final-form, uform, rc-form, formal。

F name="email" validate={...}>
    ...
</F>

  formik, NoForm, formal 支持校驗(yàn)規(guī)則外置,這樣的設(shè)計(jì)應(yīng)該是基于“關(guān)注點(diǎn)分離”的原則,JSX 用于組織 UI,校驗(yàn)規(guī)則并不是 UI 的固有屬性應(yīng)該分離出來(lái),從而進(jìn)一步降低耦合性,目的是幫助我們寫出清晰、易維護(hù)的代碼。

  formik 官方推薦使用 Yup(一個(gè)功能強(qiáng)大的規(guī)則校驗(yàn)工具,鏈?zhǔn)斤L(fēng)格的 API 設(shè)計(jì)),并針對(duì) Yup 做了優(yōu)化,提供 validationSchema 屬性方便接入。

  NoForm 提供了 validateConfig 用于外置校驗(yàn)規(guī)則,需按規(guī)則傳入 JSON 對(duì)象,用 async-validator 作為校驗(yàn)工具。

const validateRules = {
  email: string()
    .email('Invalid email')
    .required('Required'),
  // ...
}

<Form validateRules={validateRules}>
    <F name="email">
        ...
    </F>
</Form>

  動(dòng)態(tài)校驗(yàn)

  有一類場(chǎng)景例如注冊(cè)需要輸入的兩次密碼一致。還有一類場(chǎng)景是表單有聯(lián)動(dòng),選擇了 B 后,C 需要必填。我把這些稱為 動(dòng)態(tài)校驗(yàn),表單校驗(yàn)會(huì)依賴用戶的輸入 或是 表單聯(lián)動(dòng)帶來(lái)的校驗(yàn)規(guī)則的改變。

  formik 推薦的 Yup 提供了 ref 獲取其他字段的引用。

  let schema = object({

  baz: ref('foo.bar'),

  foo: object({

  bar: string(),

  }),

  x: ref('$x'),

  });

  schema.cast({ foo: { bar: 'boom' } }, { context: { x: 5 } });

  // => { baz: 'boom', x: 5, foo: { bar: 'boom' } }

  NoForm 的 validateConfig 支持動(dòng)態(tài)配置。

  const validateConfig = {

  username: {type: "string", required: true},

  age: (values, context) => { // dynamic validate config

  const { username } = values;

  return {type: "string", required: !!username };

  }

  }

  其他方案都提供了類似 values 的字段供回調(diào)函數(shù)拿到所有字段值用于處理邏輯,例如 react-final-form validate 的 allValues, uform 的 x-rules,rc-form validateFields 的 values 等等。除此之外 uform 還引入了 effects 的概念來(lái)解決聯(lián)動(dòng)問題。

  處理聯(lián)動(dòng)

  數(shù)據(jù)聯(lián)動(dòng),歸根結(jié)底是字段間的相互依賴關(guān)系,同時(shí)附加了依賴動(dòng)作,同時(shí)依賴動(dòng)作的執(zhí)行是存在時(shí)序的。

  聯(lián)動(dòng)在表單中比較常見,比較理想的是框架能夠約束開發(fā)者優(yōu)雅的去處理聯(lián)動(dòng),拒絕面條式代碼。最常見的是表單項(xiàng)的顯示隱藏,NoForm 的 If、react-final-form 的 demo 都提供了類似 jsx-control-statements 的思路,更像是 JSX 函數(shù)表達(dá)式的語(yǔ)法糖。

// 偽代碼
<F label="showA" name="showA">
  <CheckBox />
</F>
<If condition={ showA === true }>
  <F label="A" name="A">
    <Input />
    </F>
</If>

  其他更復(fù)雜的聯(lián)動(dòng)場(chǎng)景只有 uform 交出了自己的答卷,在其 文檔 中有較為詳細(xì)的羅列及其解決方案,當(dāng)然解決問題的同時(shí)也引入了很多其他概念。

  數(shù)組、嵌套表單

  表單嵌套、數(shù)組字段這兩類場(chǎng)景在日常開發(fā)中也經(jīng)常遇到,表單方案對(duì)此也有不同的設(shè)計(jì),試圖幫助開發(fā)者更優(yōu)雅的處理此類場(chǎng)景。

  字段的嵌套結(jié)構(gòu)

  rc-form、react-final-form、 formik 的字段名支持點(diǎn)括號(hào)語(yǔ)法,即支持以嵌套結(jié)構(gòu)定義字段名,例如 object.a.b、array[2] 等。

<F name="object.a">
  ...
</F>

uform NoForm 則是根據(jù)節(jié)點(diǎn)的父子關(guān)系來(lái)定義字段的結(jié)構(gòu)。


<F name="object">
  <F name="a">
    ...
  </F>
</F>

  數(shù)組類型的字段

  此類場(chǎng)景一般還會(huì)伴隨著表單項(xiàng)的動(dòng)態(tài)增刪,因此 react-final-form、 formik 除了支持點(diǎn)括號(hào)外還提供了工具類 FieldArray,提升開發(fā)體驗(yàn)。具體見 formik FieldArray demo、react-final-form FieldArray demo。

  而 uform 提供了 createArrayField,NoForm 則提供了 repeater。

  性能開銷

  性能是每個(gè)框架繞不開的話題,在開始之前,我有這樣的一個(gè)思考:表單的性能問題遇到的多嘛?

  結(jié)合自己的工作經(jīng)驗(yàn),個(gè)人認(rèn)為表單性能問題通常情況下不會(huì)遇到,個(gè)別情況下會(huì)存在,例如表單嵌套,大型項(xiàng)目的配置頁(yè)等場(chǎng)景。排除特殊場(chǎng)景,一個(gè)頁(yè)面中包含大量表單項(xiàng)本身就是不合理的,這樣的設(shè)計(jì)會(huì)影響用戶體驗(yàn)(腦補(bǔ)畫面:用戶正在操作滿屏幕的表單),所以在問題成為問題之前可能不需要傾注太多精力,比較好的策略是出現(xiàn)問題解決問題,當(dāng)然有足夠的精力能夠提前知曉/解決問題也是好的。

  回過頭來(lái)在做技術(shù)調(diào)研時(shí)還是需要有一個(gè)全面的考量,性能開銷的關(guān)注點(diǎn)主要在于單個(gè)表單項(xiàng)的改動(dòng)是否會(huì)引起整個(gè)表單重新渲染,即觀察用戶在某些字段中輸入值時(shí),其他無(wú)關(guān)聯(lián)的表單項(xiàng)會(huì)不會(huì) rerender。

  經(jīng)測(cè)試 react-final-form 和 uform 做到了這點(diǎn),在內(nèi)部實(shí)現(xiàn)上均使用了發(fā)布訂閱,每個(gè)表單項(xiàng)只會(huì)訂閱和自己相關(guān)的改動(dòng),實(shí)現(xiàn)單個(gè)改動(dòng)不影響全量。其他表單方案的狀態(tài)管理至上而下,均會(huì)造成整個(gè)表單重新渲染,formik 因此提供了 FastField 用于改進(jìn)其性能,內(nèi)置 shouldComponentUpdate 阻止不必要的渲染。

  序列化

  表單的序列化常見于 動(dòng)態(tài)表單需求 或是 可視化搭建系統(tǒng)上,這類序列化場(chǎng)景主要看業(yè)務(wù)需求,通常會(huì)約定一個(gè) JSON DSL 定義數(shù)據(jù)結(jié)構(gòu)用于描述表單。

  這類需求一般比較偏業(yè)務(wù),通用的可能并不好用,所以框架上能做的并不多,有個(gè)別框架提供了自己的方案,比較有名的是 react-jsonschema-form, uform 也有自己的理解并提供了 Form Schema。

  其他關(guān)注點(diǎn)

  · less magic,這是一個(gè)加分項(xiàng),內(nèi)部實(shí)現(xiàn)的越簡(jiǎn)單意味著潛在的 bug 越少,調(diào)試更簡(jiǎn)單,黑魔法是一把雙刃劍。

  · 確保開源項(xiàng)目的可維護(hù)性,即作者有沒有充足的熱情持續(xù)維護(hù)下去,可以觀察遺留的 issue 數(shù)量、社區(qū)討論、pr 跟進(jìn)情況等等。

  小結(jié)

  框架的內(nèi)部實(shí)現(xiàn)方式有很多,設(shè)計(jì)的選擇和權(quán)衡也不同,目標(biāo)也不同,不能一概而論。比如在我看來(lái),uform、 NoForm 想做的是一個(gè)開箱即用的方案,我什么都給你做好了,UI適配層、聯(lián)動(dòng)方案、校驗(yàn)啥都有,直接用就好;而其他方案都做的比較精簡(jiǎn),只提供基礎(chǔ)通用的部分,其他的交給開發(fā)者自行選擇設(shè)計(jì),帶來(lái)的好處是約束少可發(fā)揮空間大。

好程序員公眾號(hào)

  • · 剖析行業(yè)發(fā)展趨勢(shì)
  • · 匯聚企業(yè)項(xiàng)目源碼

好程序員開班動(dòng)態(tài)

More+
  • HTML5大前端 <高端班>

    開班時(shí)間:2021-04-12(深圳)

    開班盛況

    開班時(shí)間:2021-05-17(北京)

    開班盛況
  • 大數(shù)據(jù)+人工智能 <高端班>

    開班時(shí)間:2021-03-22(杭州)

    開班盛況

    開班時(shí)間:2021-04-26(北京)

    開班盛況
  • JavaEE分布式開發(fā) <高端班>

    開班時(shí)間:2021-05-10(北京)

    開班盛況

    開班時(shí)間:2021-02-22(北京)

    開班盛況
  • Python人工智能+數(shù)據(jù)分析 <高端班>

    開班時(shí)間:2021-07-12(北京)

    預(yù)約報(bào)名

    開班時(shí)間:2020-09-21(上海)

    開班盛況
  • 云計(jì)算開發(fā) <高端班>

    開班時(shí)間:2021-07-12(北京)

    預(yù)約報(bào)名

    開班時(shí)間:2019-07-22(北京)

    開班盛況
IT培訓(xùn)IT培訓(xùn)
在線咨詢
IT培訓(xùn)IT培訓(xùn)
試聽
IT培訓(xùn)IT培訓(xùn)
入學(xué)教程
IT培訓(xùn)IT培訓(xùn)
立即報(bào)名
IT培訓(xùn)

Copyright 2011-2023 北京千鋒互聯(lián)科技有限公司 .All Right 京ICP備12003911號(hào)-5 京公網(wǎng)安備 11010802035720號(hào)