翻譯:劉小夕
原文的篇幅非常長,不過內容太過於吸引我,還是忍不住要翻譯出來。此篇文章對編寫可重用和可維護的React組件非常有幫助。但因爲篇幅實在太長,我對文章進行了分割,本篇文章重點闡述 組合和複用。因水平有限,文中部分翻譯可能不夠準確,如果你有更好的想法,歡迎在評論區指出。
更多文章可戳: https://github.com/YvetteLau/...
———————————————我是一條分割線————————————————
組合
一個組合式組件是由更小的特定組件組合而成的。
組合(composition)是一種通過將各組件聯合在一起以創建更大組件的方式。組合是 React 的核心。
幸運的是,組合易於理解。把一組小的片段,聯合起來,創建一個更大個兒。
讓我們來看一個常見的前端應用組合模式。應用由頭部的 header
、底部的 footer
、左側的 sidebar
,還有中部的有效內容聯合而成:
<div id="root"></div>
function Application({ children }) {
return (
<div className="application box">
<Label>Application</Label>
{children}
</div>
);
}
function Header() {
return (
<div className="header box">
<Label>Header</Label>
</div>
)
}
function Footer() {
return (
<div className="header box">
<Label>Footer</Label>
</div>
)
}
function Sidebar({ children }) {
return (
<div className="sidebar box">
<Label>Sidebar</Label>
{children}
</div>
);
}
function Content({ children }) {
return (
<div className="content box">
<Label>Content</Label>
{children}
</div>
)
}
function Menu() {
return (
<div className="menu box">
<Label>Menu</Label>
<div className="description">
<div className="text shorter" />
<div className="text" />
<div className="text shorter" />
<div className="text shorter" />
</div>
</div>
);
}
function Article() {
return (
<div className="article box">
<Label>Article</Label>
<div className="description">
<div className="text shorter" /> <div className="text longer" />
<div className="text shorter" /> <div className="text" />
<div className="text shorter" /> <div className="text" />
<div className="text" /> <div className="text shorter" />
<div className="text shorter" /> <div className="text" />
<div className="text" /> <div className="text shorter" />
<div className="text shorter" /> <div className="text longer" />
<div className="text shorter" /> <div className="longer" />
<div className="text shorter" /> <div className="text" />
<div className="text" /> <div className="text shorter" />
<div className="text shorter" /> <div className="text" />
</div>
</div>
);
}
function Label({ children }) {
return <div className="label"><{children}></div>
}
const app = (
<Application>
<Header />
<Sidebar>
<Menu />
</Sidebar>
<Content>
<Article />
</Content>
<Footer />
</Application>
);
ReactDOM.render(app, document.getElementById('root'));
應用程序演示了組合如何構建應用程序。這種組織這樣組織代碼即富於表現力又便於理解。
React 組件的組合是自然而然的。這個庫使用了一個聲明範式,從而不會抑制組合式的表現力。
<Application>
由 <Header>
、 <Sidebar>
<Content>
和 <Footer>
組成. <Sidebar>
有一個 <Menu>
組件, <Content>
有一個 <Article>
組件.
那麼組合與單一責任以及封裝有什麼聯繫呢?讓我們一起看看:
單一責任原則描述瞭如何將需求拆分爲組件,封裝描述瞭如何組織這些組件,組合描述瞭如何將整個系統粘合在一起。
組合的好處
單一責任
組合的一個重要方面在於能夠從特定的小組件組成複雜組件的能力。這種分而治之的方式幫助了被組合而成的複雜組件也能符合 SRP 原則。
回顧之前的代碼片段,<Application>
負責渲染 header
、footer
、sidebar
和主體區域。
將此職責分爲四個子職責是有意義的,每個子職責由專門的組件實現,分別是<header>
、<sidebar>
、<content>
和 <footer>
。隨後,這些組件被粘合在 <Application>
。
現在來看看組合的好處:通過子組件分別實現單一職責的方式,使 <Application>
組件也符合單一責任原則。
可重用
組合有可重用的有點,使用組合的組件可以重用公共邏輯,
例如,組件 <Composed1>
和 <Composed2>
有一些公共代碼:
const instance1 = (
<Composed1>
/* Specific to Composed1 code... */
/* Common code... */
</Composed1>
);
const instance2 = (
<Composed2>
/* Common code... */
/* Specific to Composed2 code... */
</Composed2>
);
代碼複製是一個不好的實踐(例如更改 Composed1
的代碼時,也需要去更改Composed2
中的代碼),那麼如何使組件重用公共代碼?
首先,將共同代碼封裝到一個新組件中,如 <Common>
中,然後
首先,在新組件中封裝公共代碼。其次,<Composed1>
和 <Composed2>
應該使用組合的方式來包含 <Common>
組件,以避免代碼重複,如下:
const instance1 = (
<Composed1>
<Piece1 />
<Common />
</Composed1>
);
const instance2 = (
<Composed2>
<Common />
<Piece2 />
</Composed2>
);
可重用的組件符合不重複自己(Don't repeat yourself
)的原則。這種做法可以節省你的精力和時間,並且在後期,有利於代碼維護。
靈活
在 react
中,一個組合式的組件通過給子組件傳遞 props
的方式,來控制其子組件。這就帶來了靈活性的好處。
例如,有一個組件,它需要根據用戶的設備顯示信息,使用組合可以靈活地實現這個需求:
function ByDevice({ children: { mobile, other } }) {
return Utils.isMobile() ? mobile : other;
}
<ByDevice>{{
mobile: <div>Mobile detected!</div>,
other: <div>Not a mobile device</div>
}}</ByDevice>
<ByDevice>
組合組件,對於移動設備,顯示: Mobile detected!
; 對於非移動設備,顯示 Not a mobile device"
。
高效
用戶界面可組合的層次結構,因此,組件的組合是一種構建用戶界面的有效的方式。
注:DRY
原則理論上來說是沒有問題的,但在實際應用是切忌死搬教條。它只能起指導作用,沒有量化標準,否則的話理論上一個程序每一行代碼都只能出現一次才行,這是非常荒謬的,其它的原則也是一樣,起到的也只是指導性的作用。
複用
可重用的組件,一次編寫多次使用。
想象一下,如果軟件開發總是重複造輪子。那麼當你編寫代碼時,不能使用任何已有庫或工具。甚至在同一個應用中你都不能使用已經編寫過的代碼。在這種環境中,是否有可能在合理的時間內編寫出一個應用呢?絕無可能。
此時應該到認識重用的重要性,使用已有的庫或代碼,而不是重複造輪子。
應用內的複用
根據“不要重複自己”(DRY
)原則,每一條知識都必須在系統中具有單一,明確,權威的表示。這個原則建議避免重複。
代碼重複增加了複雜性和維護工作,但沒有增加顯著的價值。邏輯更新迫使您修改應用程序中的所有重複代碼。
重複問題可以用可複用組件來解決。一次編寫,多次使用。
但是,複用並非毫無成本。只有一個組件符合單一責任原則並且具有合理的封裝時,它是可複用的。
符合單一職責原則是必須的:
複用一個組件實際上就意味着複用其職責
只有一個職責的組件是最容易複用的。
但是,當一個組件錯誤地具有多個職責時,它的複用會增加大量的開銷。你只想複用一個職責實現,但也會得到不必要的職責實現。比如,你只是想要一個香蕉,但是在你得到一個香蕉的同時,不得不被迫接受所有的叢林。
合理封裝的組件。隱藏了內部實現,並且有明確的 props
,使得組件可以使用與多種需要複用的場合。
複用第三方庫
某個工作日,你剛剛收到了爲應用增加新特性的任務,在撩起袖子狂敲代碼之前,先稍等幾分鐘。
你要做的工作在很大概率上已經被解決了。由於 React
非常流行以及其非常棒的開源社區,先搜索一下是否有已存在的解決方案是明智之舉。
查看 brillout/awesome-react-components
,它有一個可複用的組件列表。
優秀的第三方庫有結構性的影響,並且會推廣最佳實踐。以我的經驗而言,最有影響的當屬 react-router
和 redux
。
react-router
使用了聲明式的路由來構建一個單頁應用。使用 <Route>
將 URL
和組件關聯起來。當用戶訪問匹配的 URL
時,路由將渲染相應的組件。
redux
和 react-redux
引入了單向和可預測的應用狀態管理。可以將異步的和非純的代碼(例如 HTTP
請求)從組件中提取出來,從而符合單一職責原則並創建出 純(pure)組件 或 幾乎純(almost-pure)的組件。
這裏是一份檢查清單可以確定第三方庫是否值得使用,:
- 文檔:檢查庫是否具備有意義的
README.md
文件和詳細的文檔 - 測試過的:可信賴庫的一個顯著特徵就是有高的測試覆蓋率
- 維護:看看庫作者創建新特性、修改bug及日常維護的頻率
最後謝謝各位小夥伴願意花費寶貴的時間閱讀本文,如果本文給了您一點幫助或者是啓發,請不要吝嗇你的贊和Star,您的肯定是我前進的最大動力。https://github.com/YvetteLau/...
關注小姐姐的公衆號,加入交流羣。