API 設計方法
我們深入了解了 Material UI 的使用方式,而 v1 的重寫讓我們能夠完全重新思考組件 API。
API 設計很難,因為您可以使其看起來簡單,但實際上卻是具有欺騙性的複雜,或者使其實際上簡單但看起來很複雜。 @sebmarkbage
正如 Sebastian Markbage 指出的,沒有任何抽象概念優於錯誤的抽象概念。我們提供低階組件以最大化組合能力。
組合
您可能已經注意到在組件組合方面,API 中存在一些不一致之處。為了提供一些透明度,在設計 API 時,我們一直遵循以下規則
- 使用
children
屬性是使用 React 進行組合的慣用方式。 - 有時我們只需要有限的子組件組合,例如當我們不需要允許子組件順序排列時。在這種情況下,提供顯式的屬性使實作更簡單且效能更高;例如,
Tab
組件接受icon
和label
屬性。 - API 一致性很重要。
規則
除了上述組合權衡之外,我們還強制執行以下規則
展開
未明確記錄的組件屬性會被展開到根元素;例如,className
屬性會應用於根元素。
現在,假設您想要禁用 MenuItem
上的漣漪效果。您可以利用展開行為
<MenuItem disableRipple />
disableRipple
屬性將會這樣傳遞:MenuItem
> ListItem
> ButtonBase
。
原生屬性
我們避免記錄 DOM 支援的原生屬性,例如 className
。
CSS 類別
所有組件都接受 classes
屬性來自訂樣式。類別設計回答了兩個約束條件:使類別結構盡可能簡單,同時又足以實作 Material Design 指南。
- 應用於根元素的類別始終稱為
root
。 - 所有預設樣式都分組在一個類別中。
- 應用於非根元素的類別會以元素名稱作為前綴,例如 Dialog 組件中的
paperWidthXs
。 - 布林屬性應用的變體沒有前綴,例如
rounded
屬性應用的rounded
類別。 - 枚舉屬性應用的變體有前綴,例如
color="primary"
屬性應用的colorPrimary
類別。 - 一個變體具有一個層級的特異性。
color
和variant
屬性被視為變體。樣式特異性越低,覆蓋就越簡單。 - 我們提高了變體修飾符的特異性。對於偽類別(
:hover
、:focus
等),我們已經必須這樣做。這允許更多的控制,但代價是更多的樣板程式碼。希望這也更直觀。
const styles = {
root: {
color: green[600],
'&$checked': {
color: green[500],
},
},
checked: {},
};
巢狀組件
組件內部的巢狀組件具有
- 它們自己的扁平化屬性,當這些屬性是頂層組件抽象的關鍵時,例如
Input
組件的id
屬性。 - 它們自己的
xxxProps
屬性,當使用者可能需要調整內部渲染方法的子組件時,例如,在內部使用Input
的組件上公開inputProps
和InputProps
屬性。 - 它們自己的
xxxComponent
屬性,用於執行組件注入。 - 它們自己的
xxxRef
屬性,當您可能需要執行命令式操作時,例如,公開inputRef
屬性以訪問Input
組件上的原生input
。這有助於回答問題 「我該如何訪問 DOM 元素?」
屬性命名
布林值
布林屬性的預設值應為
false
。這樣可以實現更好的簡寫表示法。考慮一個預設啟用的輸入範例。您應該如何命名控制此狀態的屬性?它應該被稱為disabled
❌ <Input enabled={false} /> ✅ <Input disabled />
如果布林值的名稱是一個單詞,它應該是一個形容詞或名詞,而不是動詞。這是因為屬性描述的是狀態而不是動作。例如,輸入屬性可以由狀態控制,而狀態不會用動詞描述
const [disabled, setDisabled] = React.useState(false); ❌ <Input disable={disabled} /> ✅ <Input disabled={disabled} />
受控組件
大多數受控組件都由 value
和 onChange
屬性控制。open
/ onClose
/ onOpen
組合也用於顯示相關狀態。在事件較多的情況下,名詞在前,動詞在後——例如:onPageChange
、onRowsChange
。
布林值 vs. 枚舉
有兩種選項可以為組件的變體設計 API:使用布林值;或使用枚舉。例如,我們以具有不同類型的按鈕為例。每個選項都有其優缺點
選項 1 布林值
type Props = { contained: boolean; fab: boolean; };
此 API 啟用簡寫表示法:
<Button>
、<Button contained />
、<Button fab />
。選項 2 枚舉
type Props = { variant: 'text' | 'contained' | 'fab'; };
此 API 更為冗長:
<Button>
、<Button variant="contained">
、<Button variant="fab">
。但是,它可以防止使用無效的組合,限制公開的屬性數量,並且可以輕鬆地在未來支援新值。
Material UI 組件根據以下規則結合使用了這兩種方法
- 當需要 2 個可能值時,使用布林值。
- 當需要 > 2 個可能值時,或者如果未來可能需要額外的可能值時,使用枚舉。
回到之前的按鈕範例;由於它需要 3 個可能值,我們使用枚舉。
Ref
ref
被轉發到根元素。這表示,在不透過 component
屬性更改渲染的根元素的情況下,它會被轉發到組件渲染的最外層 DOM 元素。如果您透過 component
屬性傳遞不同的組件,則 ref 將會附加到該組件。
詞彙表
- host component:在
react-dom
上下文中的 DOM 節點類型,例如'div'
。另請參閱 React Implementation Notes。 - host element:在
react-dom
上下文中的 DOM 節點,例如window.HTMLDivElement
的實例。 - outermost:從上到下讀取組件樹時的第一個組件,即廣度優先搜尋。
- root component:渲染 host component 的最外層組件。
- root element:渲染 host component 的最外層元素。