摘要:擴(kuò)展單一職責(zé)原則又稱單一功能原則,面向?qū)ο笪鍌€(gè)基本原則之一。馬丁表示此原則是基于湯姆狄馬克和的著作中的內(nèi)聚性原則發(fā)展出的。
[解讀]Thinking in React
前言原文:http://facebook.github.io/react/docs/thinking-in-react.html
Thought is the seed of action
這是放置在官方的QUICK START中的一篇博文,文章的目的是教會(huì)我們用React的方式去思考如何構(gòu)建一個(gè)應(yīng)用。
本文并非為了翻譯,而是注重表達(dá)自己學(xué)習(xí)過程中的解讀,加深對React組件化開發(fā)方式的認(rèn)知,如果需要查看原文的翻譯,可以戳這里
原文的翻譯有點(diǎn)坑,個(gè)人覺得譯文有些地方并沒有準(zhǔn)確地表達(dá)原文的意思,甚至有些錯(cuò)誤
理解React的組件化開發(fā)假如我們要構(gòu)建一個(gè)這樣的應(yīng)用
后臺(tái)已經(jīng)有JSON API提供這樣的數(shù)據(jù)
[ {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"}, {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"}, {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"}, {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"}, {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"}, {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"} ];
接下來,我們分為5步來構(gòu)建這樣的一個(gè)商品搜索的應(yīng)用。
步驟1:將UI拆分成組件樹在此步驟,我們要完成這樣的一個(gè)過程:
那么問題來了,如何劃分組件?文章給出了兩個(gè)思考這個(gè)問題的角度。
1. 單一功能原則
舉例來說,在寫程序的時(shí)候,通常為了實(shí)現(xiàn)某一單一的功能而創(chuàng)建一個(gè)函數(shù)或者一個(gè)對象,劃分組件也是類似的一個(gè)思路。單一功能原則,指的是在理想狀態(tài)下一個(gè)組件應(yīng)該只做一件事情。當(dāng)一個(gè)組件的功能變多了,就應(yīng)該拆分成若干個(gè)小的組件。
> 擴(kuò)展:單一職責(zé)原則(SRP:Single responsibility principle)又稱單一功能原則,面向?qū)ο笪鍌€(gè)基本原則(SOLID)之一。它規(guī)定一個(gè)類應(yīng)該只有一個(gè)發(fā)生變化的原因。該原則由羅伯特·C·馬?。≧obert C. Martin)于《敏捷軟件開發(fā):原則、模式和實(shí)踐》一書中給出的。馬丁表示此原則是基于湯姆·狄馬克(Tom DeMarco)和Meilir Page-Jones的著作中的內(nèi)聚性原則發(fā)展出的。
筆者認(rèn)為,運(yùn)用這一原則可以定位到應(yīng)用的最小功能模塊,從而劃分出最低層的組件。然而,這一原則并不能完全概況組件化開發(fā)的理念,單一職責(zé)原則實(shí)質(zhì)上提供的是模塊化的思想,指導(dǎo)開發(fā)者編寫低耦合、高內(nèi)聚的代碼。組件化則是一個(gè)更為復(fù)雜的概念:組件有層級(jí)關(guān)系,父子組件之間還會(huì)涉及數(shù)據(jù)傳遞(有時(shí)候是雙向的)。如圖所示:
2. 數(shù)據(jù)與UI的對應(yīng)關(guān)系
用戶的界面和數(shù)據(jù)模型在信息構(gòu)造(information architecture)方面具有一致性,即用戶界面可以很好地映射到一個(gè)構(gòu)建正確的JSON數(shù)據(jù)模型上。因此在將用戶界面劃分成組件的時(shí)候,就是將其劃分成能與數(shù)據(jù)模型一一對應(yīng)的部分。
知道了如何劃分組件,我們就對原型進(jìn)行劃分
在這個(gè)APP中,有5個(gè)組件,他們分別是
FilterableProductTable(橙色):包含整個(gè)例子的容器
SearcBar(藍(lán)色):接收用戶的輸入
ProductTable(綠色):展示并且根據(jù)用戶的輸入過濾商品
ProductCategoryRow(青色):顯示每個(gè)類別信息
ProductRow(紅色):展示一行產(chǎn)品信息
實(shí)際上對ProductTable的劃分是不夠完美的,因?yàn)楸砀竦念^部(即Name、Price一行)并不是它的一部分,而是可以多帶帶劃分出來的組件。由于表格的頭部目前相對的簡單,就簡單地處理了。但是當(dāng)表格的頭部變得復(fù)雜起來的時(shí)候,講道理的話,應(yīng)該將其多帶帶劃分成組件ProductTableHeader。
這樣,我們也可以很容易得到組件樹:
FilterableProductTable
SearchBar
ProductTable
ProductCategoryRow
ProductRow
步驟2:創(chuàng)建靜態(tài)的版本為了節(jié)省篇幅,就不在此處貼出代碼了,如果需要可以參照原文中的代碼或者參照本人自己寫的demo
按照步驟1的分析,我們可以很快地建立應(yīng)用的靜態(tài)版本,每個(gè)組件目前只有一個(gè)render函數(shù),返回每個(gè)組件的html結(jié)構(gòu),同時(shí),建立了組件之間的層級(jí)關(guān)系。不過要注意兩點(diǎn):
在render函數(shù)中使用了JSX,它是JavaScript的一種語法糖,使用的目的是為了編程的便利,可以很清晰看到我們創(chuàng)建的HTML的標(biāo)簽結(jié)構(gòu),當(dāng)然還有其他的好處,在后面會(huì)提到。
render中創(chuàng)建的是虛擬的DOM節(jié)點(diǎn),而非真實(shí)的DOM節(jié)點(diǎn),理解這一點(diǎn)對理解React的高效渲染特點(diǎn)很重要。
props vs state
首先,這兩個(gè)指的都是數(shù)據(jù)(類似于數(shù)據(jù)驅(qū)動(dòng)的思想)。關(guān)于這兩個(gè),在這里做一個(gè)簡單的分辨。
1.我們在初始化組件的時(shí)候,使用的是props。這就是props很典型的使用方法,它是父組件向子組件傳遞數(shù)據(jù)的方式,在React中,數(shù)據(jù)的傳遞是單向的,也正是基于props來實(shí)現(xiàn)。
在頂層組件FilterableProductTable會(huì)把后臺(tái)的JSON數(shù)據(jù)作為props,從頂層組件一直傳遞下去,實(shí)現(xiàn)數(shù)據(jù)的單向流動(dòng)。
2.state是什么?state只存在于React組件的內(nèi)部,React中創(chuàng)建的組件,實(shí)際上是一個(gè)狀態(tài)機(jī),當(dāng)組件的state改變的時(shí)候,會(huì)觸發(fā)組件進(jìn)行重選渲染(這一過程還會(huì)涉及到虛擬DOM的差分算法,最小化DOM操作)。大致的流程(如圖所示):
明確組件的狀態(tài)的改變,是編寫組件的核心部分,在接下來的步驟3就做這樣一件事情。
步驟3:識(shí)別出最?。ǖ峭暾模┐鞺I的狀態(tài)集構(gòu)建一個(gè)正確的應(yīng)用,首先需要做的就是找出應(yīng)用的最小的狀態(tài)集。
何謂最小?狀態(tài)集中的任意一個(gè) state都不能由其他的state計(jì)算得出。用數(shù)學(xué)來描述就是狀態(tài)集中的元素線性無關(guān)。比如說一個(gè)TODO List的應(yīng)用,一個(gè)state使用數(shù)組保存了條目數(shù)據(jù),那么就不用再使用一個(gè)state來保存條目數(shù)了,因?yàn)闂l目數(shù)就是數(shù)組的長度。
回到我們的應(yīng)用中來,在我們的例子中有這么些數(shù)據(jù)(state和props都是數(shù)據(jù))
原始產(chǎn)品信息列表
用戶輸入的搜索數(shù)據(jù)
checkbox的值
搜索過濾之后的產(chǎn)品信息列表
理解了最小之后,我們來確定狀態(tài)集??梢詮膸讉€(gè)角度排除掉非state的情況:
數(shù)據(jù)是否是通過props從父組件傳遞過來的?如果是,那么很有可能不是state
數(shù)據(jù)是否會(huì)隨時(shí)間變化?如果不會(huì),那么也很有可能不是state
是否能通過組件中的props或者其他的state計(jì)算出該數(shù)據(jù)?如果是,那就不是state
分析
原始的產(chǎn)品列表信息是通過props進(jìn)行傳遞,因此不是state
用戶輸入的搜索信息和checkbox都是隨時(shí)間變化而且不能通過其他進(jìn)行計(jì)算,應(yīng)該是state
搜索過濾之后的產(chǎn)品信息列表可以通過原始產(chǎn)品信息列表、輸入框信息和checkbox計(jì)算得出,因此不是state
到這里,我們得到了應(yīng)用的狀態(tài)集
輸入框的值
checkbox的值
步驟4:確認(rèn)state存在哪個(gè)組件中擁有了狀態(tài)集之后,接下來就要確認(rèn)哪些組件擁有哪些state。
這里是譯文的一個(gè)錯(cuò)誤的地方,并非確認(rèn)state的生命周期
我們可以從幾個(gè)方面來解決這個(gè)問題:
找出那些需要基于該state進(jìn)行渲染的組件
找到這些組件的共同的祖先組件
要么是共同的祖先組件,要么是另外一個(gè)在組件樹中位于更高層級(jí)的組件應(yīng)該擁有這個(gè) state
如果找不出擁有這個(gè) state 的組件,可以創(chuàng)建一個(gè)新的組件來維護(hù)這個(gè)state,并將這個(gè)組件添加到所有需要基于該state進(jìn)行渲染的組件的上面。
回到我們的應(yīng)用中來:
ProductTable需要通過state來展示過濾產(chǎn)品信息,SearchBar需要基于state來顯示輸入的文本和checked的狀態(tài)。
它們共同的祖先組件是FilterableProductTable
結(jié)合以上所述,可以很容易得出FilterableProductTable擁有應(yīng)用的兩個(gè)state:輸入框的值和checkbox的值
步驟5:添加反向的數(shù)據(jù)流這里可以思考3個(gè)問題:
什么叫反向的數(shù)據(jù)流?
為什么要有反向的數(shù)據(jù)流?
怎么實(shí)現(xiàn)反向的數(shù)據(jù)流?
首先,在React中,數(shù)據(jù)是從頂層傳遞到底層的。如果是底層的組件通過某種方式更新了上層的組件的state,這樣就叫做反向的數(shù)據(jù)流。
結(jié)合我們的應(yīng)用來講為什么要有反向的數(shù)據(jù)流。在步驟4中,我們得出了組件FilterableProductTable中有兩個(gè)state:輸入框的值和checkbox的值,但是這兩個(gè)狀態(tài)的改變是由在組件SearchBar中的輸入框的enter事件和checkbox的change事件來觸發(fā)的,同時(shí)state的值需要從輸入框的輸入文本和checkbox的值中獲取,這就要求數(shù)據(jù)從SearchBar傳遞到FilterableProductTable中。
“理都懂”之后,來談?wù)剬?shí)現(xiàn)。我們結(jié)合一下代碼來講解一下(代碼只是大致的實(shí)現(xiàn)):
var FilterableProductTable = React.createClass({ handleSearch : function(search) { //searchText為輸入框的值,在此函數(shù)內(nèi)可以改變state this.setState(search) //之后的邏輯省略 } render : function () { return (); } }); 寫在最后); } }); var SearchBar = React.createClass({ handleEnter : function(e) { //...省略前面的判斷邏輯 /*獲取到輸入框和checkbox的值之后,利用props傳遞到父組件, 在這里實(shí)現(xiàn)了反向的數(shù)據(jù)流 */ this.props.onSearch({searchText : searchText,isChecked : false}) }, handleChange : function(e) { }, render : function() { return (
到此結(jié)束。在發(fā)布這篇博客的時(shí)候,demo并沒有全部完成,估計(jì)要過些日子才能完成了。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/79613.html
摘要:前言本文是學(xué)習(xí)這一章后的記錄,并且用實(shí)現(xiàn)其中的示例。因此得到如下結(jié)構(gòu)而數(shù)據(jù)則從頂層組件往下流動(dòng),各層提取各自數(shù)據(jù)進(jìn)行渲染。而交互的意思是,對的操作會(huì)影響應(yīng)用數(shù)據(jù),從而刷新。更新值更新值注意中使用時(shí),需要定義一個(gè)返回函數(shù)的高階函數(shù)來實(shí)現(xiàn)。 前言 ?本文是學(xué)習(xí)Thinking in React這一章后的記錄,并且用Reagent實(shí)現(xiàn)其中的示例。 概要 構(gòu)造恰當(dāng)?shù)臄?shù)據(jù)結(jié)構(gòu) 從靜態(tài)非交互版本...
摘要:,谷歌給的一份性能指南和最佳實(shí)踐。目前而言,前端社區(qū)有三大框架和。隨后重點(diǎn)講述了和兩大前端框架,給出了大量的文章教程和相關(guān)資源列表。我認(rèn)為,使用函數(shù)式編程方式,更加符合后端程序員的思路,而是更符合前端工程師習(xí)慣的框架。 showImg(https://segmentfault.com/img/bVbjQAM?w=1142&h=640); 這個(gè)是我訂閱 陳皓老師在極客上的專欄《左耳聽風(fēng)》...
摘要:,谷歌給的一份性能指南和最佳實(shí)踐。目前而言,前端社區(qū)有三大框架和。隨后重點(diǎn)講述了和兩大前端框架,給出了大量的文章教程和相關(guān)資源列表。我認(rèn)為,使用函數(shù)式編程方式,更加符合后端程序員的思路,而是更符合前端工程師習(xí)慣的框架。 showImg(https://segmentfault.com/img/bVbjQAM?w=1142&h=640); 這個(gè)是我訂閱 陳皓老師在極客上的專欄《左耳聽風(fēng)》...
摘要:本篇開始介紹自定義組件是如何渲染的。組件將自定義組件命名為,結(jié)構(gòu)如下經(jīng)過編譯后,生成如下代碼構(gòu)建頂層包裝組件跟普通元素渲染一樣,第一步先會(huì)執(zhí)行創(chuàng)建為的。調(diào)用順序已在代碼中注釋。先看圖,這部分內(nèi)容將在下回分解 前言 React 是一個(gè)十分龐大的庫,由于要同時(shí)考慮 ReactDom 和 ReactNative ,還有服務(wù)器渲染等,導(dǎo)致其代碼抽象化程度很高,嵌套層級(jí)非常深,閱讀其源碼是一個(gè)非...
摘要:在學(xué)習(xí)源碼的過程中,給我?guī)椭畲蟮木褪沁@個(gè)系列文章,于是決定基于這個(gè)系列文章談一下自己的理解。到此為止,首次渲染就完成啦總結(jié)從啟動(dòng)到元素渲染到頁面,并不像看起來這么簡單,中間經(jīng)歷了復(fù)雜的層級(jí)調(diào)用。 前言 React 是一個(gè)十分龐大的庫,由于要同時(shí)考慮 ReactDom 和 ReactNative ,還有服務(wù)器渲染等,導(dǎo)致其代碼抽象化程度很高,嵌套層級(jí)非常深,閱讀其源碼是一個(gè)非常艱辛的過...
閱讀 1462·2021-11-22 09:34
閱讀 1409·2021-09-22 14:57
閱讀 3453·2021-09-10 10:50
閱讀 1471·2019-08-30 15:54
閱讀 3717·2019-08-29 17:02
閱讀 3503·2019-08-29 12:54
閱讀 2651·2019-08-27 10:57
閱讀 3345·2019-08-26 12:24