本文適合React、Vue開發者閱讀,10分鐘不夠?那就再加10分鐘。javascript
antd被人吐槽最多的除了彩蛋以外,那應該就是Form表單了。若是須要使用Form自帶的收集校驗功能,須要使用Form.create()包裝組件,每個須要收集的值還須要getFieldDecorator進行註冊。官方文檔大量的讓人眼花繚亂的API,大機率沒有多少人讀完了整個文檔,即使讀完了,大機率也記不住。html
寫這篇文章不是爲了吐槽Form表單,固然我也並無更好的優化Form表單的方案,本文的目的是但願你們可以經過本文了解Form表單的本質,更好的使用的Form表單。vue
網絡上有一些源碼分析的文章,我的以爲收益比不高,逐條過api式的講解甚是無趣。 一句廢話歸納原理:Form.create建立一個具備註冊、收集、校驗功能的"實例"。java
咱們把這句話分紅幾個關鍵詞逐一分析:Form.create建立實例、註冊、收集、校驗 四個關鍵詞git
實例?爲何不是組件。Form.create的核心能力是建立實例this.props.form,並非建立組件。 這個實例提供一系列的方法,如註冊、收集、校驗github
那爲何要包裝組件呢?包裝組件的目的是爲了更新組件,僅此而已。api
你應該知道全部須要該實例幫助你進行收集校驗的組件,必需要經過getFieldDecorator進行修飾,一旦通過getFieldDecorator的修飾,那麼該組件的值將徹底由該實例管理。組件的更新須要組件所在上下文處執行render, 咱們知道組件的更新有兩種方式:1. 父組件更新了 2. 自身狀態改變了微信
因此進一步講,包裝組件的目的就是爲了被包裝組件的父組件更新,一旦被getFieldDecorator修飾過的組件觸發onChange事件,便會觸發這個父組件的的更新(forceUpdate),從而促使被包裝組件的render。 如:Form.create()(A) A就是咱們所說的被包裝組件網絡
getFieldDecorator的目的是爲了把須要收集的數據在實例中進行註冊,並把註冊的值同步到被getFieldDecorator修飾的組件B上。因此組件B不可以在經過value賦值,組件B的狀態將所有由getFieldDecorator託管。antd
收集校驗就更簡單了,你能夠認爲收集校驗就是這個實例提供的幾個方法而已。
若是Form.create的核心能力是建立"實例",是否是意味着能夠不用Form.create包裹組件呢? 答:是的,若是把更新組件的副能力解決掉。剛好Ant Design Vue就是這麼去作的。
起初咱們使用了和React版一致的寫法,必須使用Form.create包裹組件,但vue推崇的template語法很難再去使用,你不得不去在Vue中使用JSX,遭到了用戶的各類吐槽。 而後咱們進行了改版,將Form.create放在了Form中去執行,經過回調的方式將Form.create建立的示例傳遞回來:
<a-form :autoFormCreate="form => this.form = form">...</a-form>
複製代碼
註冊經過a-form-item添加對應屬性來劫持子元素進行註冊。
<a-form-item fieldDecoratorId="name" :fieldDecoratorOptions="{ rules: [{ required: true, message: 'xxxx !' }] }" >
<a-input/>
</a-form-item>
複製代碼
這樣一種設計他有很大的問題:
最終方案:
既然Form.create的主要能力是建立"實例",咱們能夠暫時拋開組件,先解決構建實例的問題,
createForm(options = {}) {
return new Vue(Form.create(options));
}
複製代碼
咱們在組件上提供一個靜態方法createForm
來建立這個示例,那麼有了這個和組件沒有任何關係的方法,就能夠隨時建立"實例",同一個組件中也能夠同時擁有多個"實例"。核心能力有了,但沒有副能力也是不行的,就像沒有了四肢的大腦,有心無力。 前面講了,組件的更新須要組件所在上下文處執行render,那麼問題就簡單了,咱們只須要把當前組件的上下文傳遞給這個"實例",當註冊到實例的組件須要更新時,直接調用context.$forceUpdate()
便可。代碼以下:
createForm(context, options = {}) {
return new Vue(Form.create({ ...options, templateContext: context })());
}
複製代碼
直接新增一個組件a-form-control專門用來劫持組件並註冊是一個不錯的選擇,可是我不想讓組件嵌套太深,因此咱們仍是使用a-form-item進行劫持組件,爲了可以區分須要劫持的哪些組件,咱們使用指令進行標記並傳值, 之因此使用指令是由於咱們不該該爲一個須要註冊的組件傳遞一個不相關的屬性,若是傳遞一個未經聲明的屬性,則該屬性會被掛載到dom上,若是要聲明屬性,就必須對自定義表單控件添加額外約束。而使用指令進行標記和傳值不會存在這類問題。
<a-input v-decorator="[ 'note', {rules: [{ required: true, message: 'Please input your note!' }]} ]" />
複製代碼
校驗收集和React版沒有區別,都只是"實例"的方法。
嚴格來講並非徹底不支持,若是你不須要Form的自動收集、校驗功能,是可使用雙向綁定的。 雙向綁定在某些業務場景下的確能夠節省不少代碼,但對於某些狀況下又給咱們帶來了沒必要要的麻煩。 舉一個很簡單也很常見的栗子: 在系統中同一份數據被多處組件(包含可編輯的Form)使用是常有的事情,咱們在表單中改變這份數據,同時數據的改變同步到各個相關組件中,很是easy的完成了需求。但不少時候咱們但願表單數據改變後並不須要及時的同步到其它組件中,而是當用戶點擊肯定按鈕後纔將數據同步,咱們就不得不將這份數據進行復制甚至是深複製來知足需求,甚是蛋疼。
而若是使用ant-design-vue單項數據流的方式,數據之間的流向就變得很是清晰,表單就像一個獨立的沙盒,無論沙盒中的數據如何變化,都不會影響到沙盒的外部,而沙盒經過相關API方法和外部進行交互。
最後,10分鐘精(wo)通(shi)不(biao)存(ti)在(dang)的,但但願你們可以經過本文對antd的Form有一個進一步的認知,Form依然還有不少的功能須要你們本身去探索,在這就不一一展開了,我想也沒有必要展開。若是你們有更好的方案也歡迎提issue提pr,一塊兒探討,將ant-design-vue打形成世界第二好用的Vue UI組件庫。 誰是第一好用的?你問我? 那固然也是ant-design-vue,且不接受任何異議,就是那麼自信,那麼臭不要臉。
最後的最後,給團隊微信公衆號打個廣告,微信搜索「一點大數據技術團隊」關注公衆號,你沒看錯,就是大數據,若是你對大數據感興趣,歡迎關注該公衆號,咱們每個月會從團隊內部篩選出兩篇左右的高質量原創文章。