「技術分享」以Antd為例,快速打通UI元件開發的任督二脈

語言: CN / TW / HK

theme: condensed-night-purple highlight: atelier-heath-light


前言

猶記得,我還是一個初入職場的新人,出去面試總會被問到會不會元件開發的問題。當時專案開發都使用現成的UI元件,最初用Element UI,後來換成了Antd。無論換哪種元件,都幫助節省了大量的開發時間,自己平時元件開發,最多就進行一些簡單的標題、彈窗、表格的二次封裝。總之就是,元件開發的“道行”尚淺,所以面試的時候底氣略微不足。

經過歲月的沉澱,經驗的累積,自己開發一套UI元件已經不是什麼困難事的時候。開啟Antd的原始碼,想研究一下,Antd的技術團隊,是怎麼實現我們在官網看到的這些元件的。

講一個我之前年少無知的往事。最開始使用Element,還挺困惑的。就琢磨著餓了麼不是送外賣的嘛,怎麼還提供上技術的元件庫了?後來才知道,人家的技術團隊也非常非常的厲害。

「寫過通用元件嗎?」

這道面試題的關鍵在於,通用元件怎麼寫。

系統特性

現今,UI元件庫豐富且成熟,所以可能覺得日常開發中,通用元件會寫的很少,其實不然。

每個系統,無論是業務特性、互動特性還是UI特性,都可以整理出一部分通用元件,比如標題、頁面佈局、列表、可編輯表格、模糊搜尋框等

以列表為例

Antd有現成的Table元件,但是我們實際開發中,一般列表管理頁是帶搜尋項以及資料展示的,有可能還帶搜尋重置按鈕或者搜尋匯出按鈕。

所以通用元件就有用武之地了,一次封裝,千千萬萬的列表管理頁面就都可以用一個元件搞定了。

```js {!resetAble && ( )} {exportable && ( )}

; ``` ## 功能通用 已知日常開發中的部分功能確實可以做成通用元件,那麼怎麼界定通用的邊界呢? 通用性過高會導致程式碼過於複雜,通用性過低,開發效率會變低。我一般會觀察以下兩點: 1. 用到這個功能的時候,和業務可能關係不大,UI或者互動操作,在任何業務線下都需要這樣設計,比如可編輯表格。 2. 使用頻率,這個要加一點對未來業務發展的預判。比如搜尋項中的省份和城市,需要實現模糊搜尋匹配的功能。 未來無論怎麼樣的業務,只要有省份、城市這兩項基本都需要這個功能。 ```js ; ``` ## 引數設計 通用元件,差異的部分,一般在功能設計的時候會通過外部傳參區分或者控制。所以開發通用元件,引數設計是重要的一個環節。 如果剛開始不是很擅長設計引數,可以參考Antd的引數設計,Antd的元件豐富且功能強大,所以引數考慮的也很周全。邊學邊練,效果更佳。 如圖為Antd的Input輸入框元件「平平無奇」的引數: ![](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b227e748347b4c2a8b883024b697103f~tplv-k3u1fbpfcp-zoom-1.image) # Antd元件功能賞析 電影有精彩片段賞析,Antd的元件很豐富,如果一一列舉,詳細介紹,可能我要寫到下個月,所以我選了幾個常見且基礎的元件,來看看Antd是怎麼設計這些元件的。 官網指路☞[Ant Design](http://ant.design/components/overview-cn/) ## 賞析前準備 學習第三方元件之前,不能盲目看程式碼,可能會找不到重點或者被大量的邏輯繞暈。我一般學習之前先做三方面準備: - 先明確元件要實現什麼功能,比如輸入框是否不可操作,是否回顯資料等; - 然後看元件引數,把引數分為控制UI佈局、控制內容展示、控制操作功能等幾種;比如通過disabled的值控制輸入框是否可以操作,通過設定value的值進行資料回顯等; - 最後去思考這些引數怎麼實現具體的功能,就比較容易想清楚了。 ## Grid 柵格 柵格化佈局,基於行(row)和列(col)來定義資訊區塊,可以將區域24等分。通過 row 在水平方向建立一組 column,內容放置於 col 內。 ### 展示層 看col檔案中這三行程式碼,和各種style、className變數。不難發現,柵格化佈局主要是通過元件引數對樣式的控制來實現的。 ```js return (
{children}
); ``` ### 佈局設計 結合引數說明和程式碼分析,可以大致總結出柵格佈局的設計如下: 1.柵格元件基於 Flex 佈局。 2.柵格的佔位格數,也是它的寬度,樣式實現時使用百分比,比如span的值為6時,24等分之後,它的百分比是25%。 ``` .ant-col-6 { display: block; flex: 0 0 25%; max-width: 25%; } ``` 3.區塊間隔格數的值實際上是設定的padding值的2倍,是相鄰兩個模組的間距之和。所以程式碼中進行了除以2的處理。 ``` if (gutter && gutter[0] > 0) { const horizontalGutter = gutter[0] / 2; mergedStyle.paddingLeft = horizontalGutter; mergedStyle.paddingRight = horizontalGutter; } ``` 4.響應式佈局,支援六個響應尺寸:xs、sm、md、lg、xl、xxl。引數支援多型別可以是number型別,也可以是Object型別。使用typeof判斷引數型別。 ``` if (typeof propSize === 'number') { sizeProps.span = propSize; } else if (typeof propSize === 'object') { sizeProps = propSize || {}; } ``` 佈局功能分析告一段落,柵格元件賞析也就收工了。 ## Steps 步驟條 我們來看看步驟條的功能。 - 步驟條狀態,已完成、進行中、未開始、執行錯誤。 - 兩種展示方式,橫向和縱向。 - 不同展示型別,數值類、自定圖示類、點狀類。 - 內容展示,標題、子標題、詳情描述。 ### rc-steps 我在看Antd的原始碼時發現,有些元件底層用的第三方[react-component](http://github.com/react-component)中的元件。當然這個元件庫也是屬於Antd的。所以想研究Steps元件的功能,需要翻另一個元件庫的程式碼[react-componentr/steps](http://github.com/react-component/steps)。 ```js import RcSteps from 'rc-steps'; ``` ### 步驟條狀態 既可以通過status直接指定當前步驟狀態,也可以通過對比current和步驟的數值確定步驟的狀態。 ```js const stepNumber = initial + index; if (status === 'error' && index === current - 1) { childProps.className = `${prefixCls}-next-error`; } if (!child.props.status) { if (stepNumber === current) { childProps.status = status; } else if (stepNumber < current) { childProps.status = 'finish'; } else { childProps.status = 'wait'; } } ``` ### 展示型別 步驟條支援多種不同的展示型別,程式碼實現上主要是通過條件語句判斷。 - 點狀型別,支援自定義展示。當點狀步驟條引數progressDot的值是函式型別時,會使用傳入的值;否則使用內部定義的點狀展示內容。 - 自定義圖示,引數icon表示步驟圖示的型別,當它有值的時候,步驟條會顯示成它的值。有兩個特殊的圖示:成功狀態、失敗狀態,這兩個狀態的圖示如果使用元件時沒有進行自定義,會取內部定義的圖示。 - 預設型別,放到條件判斷最底層,當其他判斷條件的引數沒有值時,步驟條會展示內部定義的預設型別。 **條件判斷** ![](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/17d16ca3f30b47e29c6a8a0e5c68661a~tplv-k3u1fbpfcp-zoom-1.image) **內部定義的成功和失敗的圖示** ```js const icons = { finish: , error: , }; ``` ## Table 表格 Antd的Table表格,功能很強大,單看文件中的使用介紹就能感覺出來,可用功能大概30多種。我帶著這些功能是怎樣實現的好奇心,研究了Antd的原始碼。內容有點多,我挑基礎的部分講一講。 ![](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ffbd4527953f44c2a74dd59760f64788~tplv-k3u1fbpfcp-zoom-1.image) ### rc-table Table元件,底層主要使用[react-component](http://github.com/react-component)中的[table](http://github.com/react-component/table)元件。 ### columns - 引數columns表示表格列的配置描述,表格有哪些列表項都是通過它定義的。 - Tabel元件會將columns傳入RcTable元件。 - columns的值確定表頭thead都有哪些分組。 - tbody中表格項的值,也是通過columns中列表項的dataIndex變數,從引數dataSource中找到對應的值。 ```js {flattenColumns.map((column: ColumnType, colIndex) => { const { render, dataIndex, className: columnClassName } = column; return ( ); })} ``` ### dataSource - Table的引數dataSource實現表格資料回顯。 - dataSource傳入Tabel元件會根據分頁功能處理成pageData物件,傳入RcTable元件。 - 在RcTable元件中,表格列展示內容是封裝到子元件Body中的。元件Body會先迴圈渲染表格的行資料,每一行下面包含一個BodyRow子元件 - BodyRow子元件,行資料會進行迴圈單元格資料,而單元格的內容封裝在Cell子元件中。 - Cell單元格元件中,結合columns中的dataIndex確定最終回顯的值。 其中單元格的標籤會根據傳入的component的值不同,使用不同的標籤,預設為td,表頭thead傳入的為tr。 ```js component: Component = 'td', ...... return ( {appendNode} {mergedChildNode} ); ``` Table元件比較複雜,功能比較豐富,元件的顆粒度也很細,我研究columns和dataSource就花了不少時間,更多的功能,後面再慢慢探索吧。 # 總結 多看一些優秀的專案原始碼,可以幫助拓展開發思路,提升技術設計思維。 現在有Antd等優秀的UI元件庫,好像是不用重複造輪子了。但是奔著學習的目的,去開發一套UI元件還是可以幫助提升技術的。當然這些都是給初級開發者的建議,大佬們,大佬們的技術能力,我還在努力追趕。 元件系列的分享告一段落,後面想換換思維,學習一下游戲開發。下個系列——「記憶力的小遊戲」見咯~ 我正在參與掘金技術社群創作者簽約計劃招募活動,[點選連結報名投稿](http://juejin.cn/post/7112770927082864653)。 > **輕鬆一笑** > > 聚餐,來兩盤土豆絲,為什麼是土豆絲,因為便宜;為什麼兩盤,因為一盤不夠。