React之智能組件和木偶組件


智能組件 VS 木偶組件

在 React + Redux 結合作為前端框架的時候,提出了一個將組件分為“智能”和“木偶”兩種

  • 智能組件:它是數據的所有者,它擁有數據、且擁有操作數據的action,但是它不實現任何具體功能。它會將數據和操作action傳遞給子組件,讓子組件來完成UI或者功能。這就是智能組件,也就是項目中的各個頁面。
  • 木偶組件:它就是一個工具,不擁有任何數據、及操作數據的action,給它什么數據它就顯示什么數據,給它什么方法,它就調用什么方法,比較傻。這就是木偶組件,即項目中的各個組件。

因此,數據在page層獲取、數據的操作和處理在page層定義,組件就是傻傻的知道執行和顯示就行了,各操各的心。這種設計也整好符合目前無論是Vue、augular還是React提倡的基於數據驅動的設計理念——程序定義好Model和View的關系,剩下的業余處理只需要關心數據變化,View的變化由框架自動執行,無需像jquery時代再去手動操作DOM。

數據管理

下面看一下數據在系統中是如何傳遞的。這里的數據分為兩種:

  • 第一種可以稱之為“系統數據”,和業務無關的,一般任何系統中都會有。例如用戶id、用戶頭像、配置信息等,這個的特點就是符合單例模式,全局共用一套
  • 第二種可以稱之為“業務數據”,每個頁面都可能不一樣。如在娛樂頻道需要的是娛樂新聞列表,而到了軍事頻道就需要軍事新聞的列表了,再到新聞詳情頁就需要新聞的內容了

針對這兩種不同的數據,當然要分開處理。針對第一種“系統數據”,系統一初始化就立即獲取,然后交給Redux做管理,這也符合Redux的特點。而針對第二種“業務數據”,那就什么時候用,就什么時候獲取。


代碼架構(物理)

使用React + Redux設計代碼結構,目前方式都比較統一,基本都是按照以下方式來設計,這些在github等社區都能找到相似的結構。

app // 業務代碼目錄,或者叫 src --actions // 定義 Redux 的各個 action --components // 定義項目中的各個組件,里面可能有很多個子文件夾 --config // 項目配置,無具體規定,自由發揮 --constants // 定義 Redux 中用到的各個常量 --container // 定義項目中的所有的頁面 --fetch // 定義項目中所有數據獲取、提交的方法 --reducers // 定義 Redux 的 reducer 規則 --router // 定義項目中的 router 規則 --store // 定義 Redux 的全局 store 對象 --util // 工具函數,例如時間格式的處理等 `--index.jsx // 入口,被 ../index.html 引用 resource // 靜態資源目錄,或者叫 static mocha // 測試用例,一般叫 test ,但是使用 fis3 時候 test 文件夾有其他用處,不得不換個名字 .eslintignore .eslintrc.json .gitignore fis-conf.js index.html package.json README.md

以上目錄中,app/componentsapp/container是開發中修改最多的目錄。app/container里面定義的都是頁面,即智能組件,只關心數據,功能比較單一,因此結構也比較簡單

app/container list index.jsx Home.jsx Baijia.jsx detail detail.jsx imgDetail.jsx

但是針對組件app/components來說,要顯示樣式,當然就需要css和圖片,文件類型比較多。因此,要按照如下方式定義組件

app/components LoadMore img icon.png index.jsx style.less BannerAd img close.png index.jsx style.less

即,用一個文件夾來表示單個組件,里面的index.jsx是業務和模板代碼,style.less是樣式,img/文件夾放圖片文件。這樣的話,可以把每個組件都作為一個整體來管理,而且引用的時候也比較方便,例如import LoadMore from '../../app/components/LoadMore'就可以了——目錄名即組件的名字。


使用React重構百度新聞webapp前端  https://www.imooc.com/article/12433 

 ————————————————————————————————————————————————————————————————————————

 

Redux 的 React 綁定庫包含了 容器組件和展示組件相分離 的開發思想。明智的做法是只在最頂層組件(如路由操作)里使用 Redux。其余內部組件僅僅是展示性的,所有數據都通過 props 傳入。

那么為什么需要容器組件和展示組件相分離呢?

這里有個基本原則:容器組件僅僅做數據提取,然后渲染對應的子組件,記住這個點,Trust me!

看下面這個展示列表的例子,不區分容器和展示組件的情況

// CommentList.js class CommentList extends React.Component { constructor() { super(); this.state = { comments: [] } } componentDidMount() { $.ajax({ url: "/my-comments.json", dataType: 'json', success: function(comments) { this.setState({comments: comments}); }.bind(this) }); } render() { return <ul> {this.state.comments.map(renderComment)} </ul>; } renderComment({body, author}) { return <li>{body}—{author}</li>; } } 
  • 可用性:CommentList不可以復用

  • 數據結構:組件應該對所需要的數據有所預期,但這里其實沒有,PropTypes可以很好的做到這一點

那么來看下分離的情況:

// CommentListContainer.js class CommentListContainer extends React.Component { constructor() { super(); this.state = { comments: [] } } componentDidMount() { $.ajax({ url: "/my-comments.json", dataType: 'json', success: function(comments) { this.setState({comments: comments}); }.bind(this) }); } render() { return <CommentList comments={this.state.comments} />; } } // CommentList.js class CommentList extends React.Component { constructor(props) { super(props); } render() { return <ul> {this.props.comments.map(renderComment)} </ul>; } renderComment({body, author}) { return <li>{body}—{author}</li>; } } 

這樣就做到了數據提取和渲染分離,CommentList可以復用,CommentList可以設置PropTypes判斷數據的可用性

來看下容器組件和展示組件的區別:

展示組件 容器組件
關注事物的展示 關注事物如何工作
可能包含展示和容器組件,並且一般會有DOM標簽和css樣式 可能包含展示和容器組件,並且不會有DOM標簽和css樣式
常常允許通過this.props.children傳遞 提供數據和行為給容器組件或者展示組件
對第三方沒有任何依賴,比如store 或者 flux action 調用flux action 並且提供他們的回調給展示組件
不要指定數據如何加載和變化 作為數據源,通常采用較高階的組件,而不是自己寫,比如React Redux的connect(), Relay的createContainer(), Flux Utils的Container.create()
僅通過屬性獲取數據和回調  
很少有自己的狀態,即使有,也是自己的UI狀態  
除非他們需要的自己的狀態,生命周期,或性能優化才會被寫為功能組件  

優勢:

  • 展示和容器更好的分離,更好的理解應用程序和UI

  • 重用性高,展示組件可以用於多個不同的state數據源

  • 展示組件就是你的調色板,可以把他們放到單獨的頁面,在不影響應用程序的情況下,讓設計師調整UI

  • 迫使你分離標簽,達到更高的可用性

React 之容器組件和展示組件相分離解密  https://segmentfault.com/a/1190000006845396

 

————————————————————————————————————————————————————————————————————————

 

智能組件和木偶組件(也有叫容器組件和UI組件的)。見名知意,智能組件就要做一些比較智能比較‘高端’的工作,智能組件負責邏輯處理、數據獲取等比較‘智能’的工作; 
而木偶組件就比較‘呆板’,它原則上只負責展示功能,像一只木偶,別人(智能組件)用線牽着它來控制它的展示內容。

來看一個例子:

class ListUI extends React.Component{ render(){ let data = this.props.data; return ( <ul> {data.map(item => <li key={item.id} >{item.text}</li>)} </ul> ) } } class List extends React.Component{ constructor(){ super(); this.getData = this.getData.bind(this); } render(){ let data = this.getData(); return <ListUI data={data} /> } getData(){ return [{id : 1,text : 'hello'},{id : 2,text : 'world'}]; } }

本例中組件List(智能組件)負責獲取數據(getData),實際開發中會比這復雜的多,涉及到調用接口獲取數據,數據的處理等等,本例側重說明概念,具體的就不再贅述。 
而組件ListUI(木偶組件)則只負責數據的展示,傻瓜式的別人給他什么他就展示什么。

這是react開發中很重要的一個概念,這種解構分工明確,解構清晰,方便維護和擴展。實屬react開發必備之技能。

本例中還有一點值得注意: 
構造函數中的綁定this 實際開發中會在一個組件中定義很多方法,這些方法中可能會有回調函數,這就會導致this指向的問題(當然ES6中的箭頭函數能規避這個問題,也推薦只用ES6的寫法) 
但是也不排除有ES5的寫法存在,所以需要我們綁定this,這里強烈推薦在構造函數中統一的綁定this,這樣哪些方法綁定了this就一目了然也便於維護。

基於這個概念做一些擴展。 
事件處理: 有了以上概念的基礎,現給組件添加一個點擊事件,目的是點擊li時彈出當前li的文本內容。根據以上的概念,要把邏輯處理的方法寫在智能組件中,然后把方法作為一個prop傳遞給木偶組件,最后在木偶組件中添加點擊事件來調用傳過來的方法。 
代碼如下:

class ListUI extends React.Component{ constructor(){ super(); this.clickHandler = this.clickHandler.bind(this); } render(){ let data = this.props.data; return ( <ul> {data.map(item => <li key={item.id} onClick={this.clickHandler}>{item.text}</li>)} </ul> ) } clickHandler(e){ this.props.clickHandler(e); } } class List extends React.Component{ constructor(){ super(); this.getData = this.getData.bind(this); this.clickHandler = this.clickHandler.bind(this); } render(){ let data = this.getData(); return <ListUI data={data} click={this.clickHandler}/> } getData(){ return [{id : 1,text : 'hello'},{id : 2,text : 'world'}]; } clickHandler(e){ let ele = e.currentTarget || e.srcElement; alert(ele.innerHTML); } }

最后想說的是:智能組件和木偶組件這種編程思想非常重要,開發大型項目時體現的尤為明顯,當項目很大時更需要把木偶組件細分,不然代碼會雜亂無章,非常的不便於閱讀,及其的不利於維護和擴展,最后的結果是項目無法進行。

摘自: https://blog.csdn.net/hkwBest/article/details/78688205?locationNum=6&fps=1 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM