您好,登錄后才能下訂單哦!
一、易于維護組件的設計要素
1.組件劃分的原則:高內聚低耦合
(1)高內聚:將邏輯緊密相關的內容放在一個組件內。React可以將展示內容的JSX、定義行為的JavaScript代碼、甚至定義樣式的css,都可以放在一個JavaScript文件中,因此React天生具有高內聚的特點。
(2)低耦合:不同組件之間的依賴關系要盡量弱化,也就是每個組件要盡量獨立。
二、React組件的數據
1.數據分類:
React組件的數據分為兩種:prop和state。無論prop或者state改變,都可能引發React的重新渲染。那在設計一個組件時,什么時候選擇prop什么時候選擇state呢?原則很簡單:prop是組件的對外接口,state是組件的內部狀態,對外用prop,內部用state。
2.Prop:property的縮寫,是從外部傳遞給組件的數據,一個React組件通過定義自己能夠接受的prop就定義了自己的對外公共接口。我們先從外部世界看prop是如何使用的:
<SampleButton id="sample" borderWidth={2} onClick={onButtonClick} style={{color:"red"}}/> 上面創建了名為SampleButton的組件實例,使用了名字分別為id、borderWidth、onClick和style的prop。此處注意:HTML組件屬性的值都是字符串類型,即使是內嵌JavaScript,也依然是字符串形式表示代碼。但React組件的prop所能支持的類型除了字符串、可以是任何一種JavaScript語句支持的數據類型,如:數字類型、函數類型、style的值是一個包含color字段的對象,當prop的類型不是字符串類型時,在JSX中必須用花括號{}將prop值包住,所以style的值有兩層花括號,外層花括號代表的是JSX的語法,內層花括號表示這是一個對象常量。
Prop要反饋數據給外部世界,使用函數類型的prop,這等于父組件給了子組件一個回調函數,子組件在恰當的時機調用函數類型的prop,可以帶上必要額參數,這樣就把數據傳遞給外部世界。
3.React要求render函數只能返回一個元素!
4.組件內部接收傳入的prop:
(1)首先是構造函數,如下:
class Counter extends Component {
constructor(props){
super(props);
this.onClickIncrementButon = this.onClickIncrementButon.bind(this);
this.onClickDecrementButon = this.onClickDecrementButon.bind(this);
this.state = {
count : props.initValue || 0
}
}
}
注意:組件定義自己的構造函數,一定要在構造函數的第一行通過super調用父類也就是React.Component的構造函數;如果未調用,那么組件實例被構造之后,類實例的所有成員函數就無法通過this.props訪問到父組件傳過來的props值。很明顯,給this.props賦值是React.Component構造函數的工作之一。在Counter的構造函數中還給兩個成員函數綁定了當前this的執行環境,因為ES6方法創建的React類并不自動給我們綁定this到當前實例對象。
我的理解:
Component是React內的一個基類,用于繼承和創建React自定義組件。ES6規范下的面向對象實現起來非常精簡,class關鍵字可以快速創建一個類,而Component類內的所有屬性和方法均可以通過this訪問。換而言之,在Component內的任意方法內,可以通過this.xxx的方式調用該Component的其他屬性和方法。
constructor:類的默認方法,通過new命令生成對象實例時,自動調用該方法。一個類必須有constructor方法,如果沒有顯示定義,一個空的constructor方法會被默認添加;
super:子類必須在constructor方法中調用super方法,否則新建實例會報錯。這是因為子類自己的this對象,必須先通過父類的構造函數完成塑造,得到與父類同樣的屬性和方法,然后再對其加工,加上子類自己的實例屬性和方法。如果不調用super方法,子類就得不到this對象(子類是沒有自己的 this 對象的,它只能繼承自父類的 this 對象,然后對其進行加工,而super( )就是將父類中的this對象繼承給子類的。沒有 super,子類就得不到 this 對象);
super(props):在constructor中可以使用this.props;
this:在ES6語法下,類的每個成員函數在執行時的this并不是和類實例自動綁定的。而在構造函數中,this就是當前組件實例。所以,為了方便將來的調用,往往在構造函數中將這個實例的特定函數綁定this為當前實例。this.onClickIncrementButon = this.onClickIncrementButon.bind(this),就是通過bind方法讓當前實例中onClickIncrementButon函數被調用時,this始終指向當前組件實例
(2)讀取prop值:
在構造函數中可以通過props獲得傳入的prop值,在其他函數中可通過this.props訪問傳入prop的值。
const {caption} = this.props;
以上,我們使用了ES6的解構賦值語法,從this.props中獲得了名為caption的prop值。
5.propTypes檢查:
組件支持哪些prop;
每個prop應該是什么樣的格式;
如:對于Counter組件的propTypes定義代碼如下:
Counter.propTypes = {
caption : propTypes.string.isRquired,
initVlue : propTypes.number
}
其中要求caption必須是string類型,initVlue必須是number類型,另外,caption帶上了isRquired,表示使用Counter組件必須指定caption,而initVlue如果沒有也沒關系。
propTypes雖然能在開發階段發現代碼中的問題,但是放在產品環境就不大合適了:首先,占用一些代碼空間,耗CPU計算資源;其次,在產品環境下做propTypes檢查沒有什么幫助,在最終用戶的瀏覽器Console中輸出這些錯誤信息沒什么意義。所以,最好的方式是,開發者在代碼中定義propTypes,但在發布產品代碼時,用一種自動的方式將propTypes去掉。現有的babel-react-optimize具有這個功能,可以通過npm安裝,但是應該確保只在發布產品代碼時使用它。
6.初始化state:
通常在組件類的構造函數結尾處初始化state,如下:
constructor(props){
......
this.state = {
count:props.initValue || 0
}
}
因為initValue是一個可選的props,考慮到父組件沒有指定這個props值的情況,我們優先使用傳入屬性的initVlue,如果沒有,就使用默認值0。
組件的state必須是一個JavaScript對象!!!
以上,可使用React的defaultProps功能,如下:
Counter.defaultProps = {0}
7.改變組件的state,如下:
onClickIncrementButton () {
this.setState({count : this.state.count + 1})
}
在代碼中,this.state可以讀取到組件當前的state。注意:改變組件state必須要使用this.setState函數而不能直接去修改this.state。直接修改this.state的值,雖然事實上改變了組件的內部狀態,但只是野蠻的修改了state,并沒有驅動組件進行重新渲染,這樣就無法反應this.state值的變化;而this.setState()函數所做的事情,首先是改變this.state的值,然后驅動組件經歷更新過程,這樣才有機會讓this.state里新的值出現在界面上。
8.prop和state的對比
三、組件的生命周期
生命周期可能會經歷3個過程:
1.裝載過程,依次調用的函數如下:
1.1 constructor
是ES6中每個類的構造函數,要創建一個組件類的實例,當然會調用對應的構造函數;
注意:無狀態的React組件不需要定義構造函數,一個組件需要構造函數,往往為下面的目的:
(1)初始化state,因為組件生命周期中任何函數都可能會訪問state,那整個生命周期中第一個被調用的構造函數自然是初始化state最理想的地方;
(2)綁定成員函數的this環境
1.2 getInitialState和getDefaultProps
待完成........
1.3 render
一個React組件必須要實現render函數,因為所有React組件的父類React.Component類對除render類之外的生命周期函數都有默認實現。
render函數并不做實際的渲染動作,而是返回一個JSX描述的結構,最終由React來操作渲染過程。某些特殊組件的作用不是渲染界面,或者,組件在某些情況下選擇沒有東西可畫,那就讓render函數返回一個null或者false,等于告訴React,這個組件這次不需要渲染任何DOM元素。
注意:render函數應該是一個純函數,完全根據this.props和this.state來決定返回的結果,而且不要產生任何副作用。在render中調用this.setState是錯的,因為一個純函數不應該引起狀態的改變。
1.4 componentWillMount和componentDidMount
在裝載過程中,componentWillMount在調用render之前被調用,componentDidMount在調用render之后被調用。
通常不用定義componentWillMount函數,所有可以在componentWillMount中做的事情都可以提前到constructor中去做。
而componentDidMount作用很大!render函數在調用完之后,componentDidMount并不是會被立刻調用,componentDidMount被調用的時候,render函數返回的東西已經引發了渲染,組件已經被裝載到了DOM樹上。由于render函數只返回一個JSX表示的對象,然后React庫根據返回對象來決定如何渲染。而React庫肯定要把所有組件返回的結果綜合起來,才知道如何產生對應的DOM修改,所以,當有多個組件時,只有React庫調用多個組件的render函數之后,才有可能完成裝載,這時才會依次調用各個組件的componentDidMount函數作為裝載過程的收尾。
兩者的區別:componentWillMount可以在服務器端被調用,也可以在瀏覽器端被調用,而componentDidMount只能在瀏覽器端被調用!
2.更新過程:
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
并不是所有的更新過程都會執行全部函數
2.1 componentWillReceiveProps(nextProps)
只要是父組件的render函數被調用,在render函數里面被渲染的子組件就會經歷更新過程,不管父組件傳給子組件的props有沒有改變,都會觸發子組件的componentWillReceiveProps。所以,這個函數有必要把傳入參數nextProps和this.props做對比,nextProps代表的是這一次渲染傳入的props值,this.props代表上一次渲染時的props值,只有兩者有變化的時候才調用this.setState更新內部狀態,這是提高React性能的重要方式。
注意:通過this.setState方法觸發的更新過程不會調用這個函數,這是因為這個函數適合根據新的props值(nextProps)來計算出是不是要更新內部狀態state。更新組件內部狀態的方法就是this.setState,如果this.setState的調用導致componentWillReceiveProps再一次被調用,那就是一個死循環了。
2.2 shouldComponentUpdate(nextProps,nextState)
render和shouldComponentUpdate是React生命周期函數中唯二兩個要求有返回結果的函數。render函數返回的結果用于構造DOM對象,而shouldComponentUpdate返回一個布爾值,告訴React庫這個組件在這次更新過程中是否要繼續。
在更新過程中,React庫首先調用shouldComponentUpdate函數,若返回true,會繼續更新過程,接著調用render函數,若是false,停止更新過程。另外,通過this.setState函數引發更新過程,并不是立刻更新組件的state值,在執行到函數shouldComponentUpdate的時候,this.state依然是this.setState函數執行之前的值,因此,在shouldComponentUpdate函數中,可進行比較nextProps、nextState、this.props、this.state是否發生變化,若變化則繼續更新,是提高React性能的重要方式。
2.3 componentWillUpdate和componentDidUpdate
如果shouldComponentUpdate返回true,React接下來會依次調用對應組件的componentWillUpdate、render和componentDidUpdate函數。
和裝載過程不同的是,當在服務器端使用React渲染時,componentDidUpdate函數并不是只在瀏覽器端才執行的。無論更新過程發生在服務器端還是瀏覽器端,該函數都會被調用。
3.卸載過程
componentWillUnmount
當React組件要從DOM樹上刪除掉之前,對應的componentWillUnmount會被調用,所以該函數適合做一些請理性的工作。
componentWillUnmount往往和componentDidMount有關,比如,在componentDidMount用非React的方法創造了一些DOM元素,如果不管可能會造成內存泄漏,那就需要在componentWillUnmount中將這些創建的DOM元素清理掉。
四、組件向外傳遞數據
1.React中state和prop的局限
React使用state來存儲狀態的一個缺點就是:數據的冗余和重復,另外使用prop在多個父子組件中傳遞數據時也有同樣的問題。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。