對於傳統的 DOM 維護,咱們的步驟多是:
1.初始化DOM結構
2.從服務器獲取新數據
3.使用新數據更新局部DOM
4.綁定各類事件 前端
首先,咱們操做 DOM 是最昂貴的開銷,對於須要反覆更新 DOM 的網頁,無疑是噩夢。而 React 引入了一個全新的概念:虛擬 DOM。虛擬 DOM 是駐留在內存裏的一種特殊的對象類型數據,咱們能夠理解爲這是真實 DOM 在內存裏的映射。除告終構上的映射外,這個虛擬的 DOM 還包括了渲染真實所須要的數據以及綁定的事件。react
虛擬 DOM 在建立時,首先是使用 JSX 的語法生成一個真實 DOM 樹的映射,其次是從服務器端拉取遠程數據,接着注入到這個虛擬DOM 樹中,同時綁定事件。好了,有了虛擬 DOM、數據、事件,萬事俱備。接下來,調用 render() 方法一次性渲染出真實的 DOM,而後全量插入到網頁中。虛擬 DOM 靜靜地躺在內存裏,等待數據更新。新數據來臨,調用 setState() 方法更新數據到虛擬 DOM 中(此過程會進行差別化對比),而後自動調用 render() 再局部更新渲染出真實的 DOM 。
1.一個虛擬DOM,對應一個真實DOM
2. 一旦數據更新,從新生成虛擬DOM,並對真實DOM進行最小差別化局部更新, 就這麼簡單。卻帶來性能上的較大提高。 web
React採用jsx語法, jsx是 JavaScript 和 HTML 的結合體, JSX 的目標是在 JavaScript 中更加方便的建立 HTML 節點。JSX 通過解釋器解釋,最終呈現標準的 JavaScript 語法,例如在 React中,下面的 JSX 代碼:編程
return ( <div className="commentBox"> Hello, world! I am a CommentBox. </div> );
將被解釋爲以下代碼:數組
return ( React.createElement( 'div', {className: "commentBox"}, "Hello, world! I am a CommentBox." ) );
能夠看到,解釋器會分析 JSX 的語義並將其轉化爲建立元素的方法,而並不是將 HTML 部分看成簡單的字符串原樣輸出,所以不用擔憂 JSX會引起 XSS。另外一方面,因爲 HTML 部分不是被看成字符串處理,JSX 中 HTML 元素的部分寫法與標準 HTML 有些許出入。瀏覽器
另外須要注意的是:因爲一條 JSX 語句只可以建立一個虛擬 HTML 節點,所以 JSX 語句中至多擁有一個根 HTML 節點。下面的 JSX 就沒法完成解釋:緩存
return ( <h1>Main Title</h1> <h2>Sub Title</h2> )
組件是 React 最基本的渲染單位,React 中全是模塊化、可組裝的組件。組件的嵌套和拼裝組成了整個頁面。JSX 語法可讓組件的構建更加方便,每一個組件擁有本身的屬性(props)和狀態(state),經過屬性賦值和狀態修改,React 就能夠實現整個頁面的呈現和交互。安全
咱們都知道,JavaScript 經過 DOM 實現與 HTML 的交互,然而咱們在 React 中構造一個元素卻調用了React.createElement方法,表面上看一樣是生成一個 DOM 對象,那麼爲何不使用document.createElement方法呢?緣由就在於React.createElement方法並無真正生成一個 DOM 對象,而是生成了一個虛擬的 DOM 對象,這也是 React 的核心思想所在。React 中的全部操做都是對虛擬 DOM 而不是對真實 DOM 的操做。因此 React 的狀態修改不會實時體如今頁面上,而是在整個組件渲染時,React 會比較組件的狀態改變,僅將發生改變DOM 進行重繪,雖然看起來這個過程十分複雜,但實踐證實這一機制確實可以提升頁面渲染效率。這正是 React 高效的祕訣之一。
React組件參數說明 服務器
當經過調用 React.createClass() 來建立組件的時候,你應該提供一個包含render方法的對象,而且也能夠包含其它的在這裏描述的生命週期方法。網絡
函數原型ReactComponent render(),
render()方法是必須的。當調用的時候,會檢測this.props和this.state,返回一個單子級組件。該子級組件能夠是虛擬的本地DOM組件(好比 <div /> 或者 React.DOM.div()),也能夠是自定義的複合組件。你也能夠返回null或者false來代表不須要渲染任何東西。實際上,React渲染一個<noscript>標籤來處理當前的差別檢查邏輯。當返回null或者false的時候this.getDOMNode()將返回null。render()函數應該是純粹的,也就是說該函數不修改組件state,每次調用都返回相同的結果,不讀寫DOM信息,也不和瀏覽器交互(例如經過使用setTimeout)。若是須要和瀏覽器交互,在componentDidMount()中或者其它生命週期方法中作這件事。保持render()純粹,可使服務器端渲染更加切實可行,也使組件更容易被理解。
函數原型 object propTypes propTypes
是React提供的一種驗證機制,該對象中定義了一系列的驗證方法,可對props
進行驗證。組件初始化時,若是傳遞的props
屬性和propTypes
不匹配,則會打印一個console.warn
日誌。
React.createClass({ propTypes: { // 驗證布爾值 optionalBool: React.PropTypes.bool, // 驗證是一個函數 optionalFunc: React.PropTypes.func, // 驗證是數字 optionalNumber: React.PropTypes.number, // 自定義驗證器,驗證失敗須要返回一個 Error 對象。不要直接 // 使用 `console.warn` 或拋異常,由於這樣 `oneOfType` 會失效。 customProp: function(props, propName, componentName) { //自定義的驗證方法 …… } , // 其它驗證 …… }, /* 其它specification... */ }); propTypes使用示例: var App = React.createClass({ propTypes: { site: React.PropTypes.shape({ domain: React.PropTypes.string.isRequired, name: React.PropTypes.string }).isRequired }, render: function() { return ( <p>站點信息-{this.props.site.name}:{this.props.site.domain}</p> ); } }); var site = { name: 4, // 不合法的類型 domain: 'itbilu.com' } ReactDOM.render( <App site={site} />, document.getElementById('example')); // 運行後會拋出如下錯誤 // Warning: Failed propType: Invalid prop `site.name` of type `number` supplied to `App`, expected `string`.
函數原型 array mixins
mixin 數組容許使用混合來在多個組件之間共享行爲。如一個組件須要按期更新,這時咱們能夠setInterval()
方法很容易的作到,但當不須要它時,咱們要取消定時器以節省內存。下面咱們使用 React 提供的生命週期方法來通知組件建立或銷燬,並結合mixin
,實現組件的定時清理:
var SetIntervalMixin = { componentWillMount: function() { this.intervals = []; }, setInterval: function() { this.intervals.push(setInterval.apply(null, arguments)); }, componentWillUnmount: function() { this.intervals.map(clearInterval); } }); var TickTock = React.createClass({ mixins: [SetIntervalMixin], // 引用 mixin getInitialState: function() { return {seconds: 0}; }, componentDidMount: function() { this.setInterval(this.tick, 1000); // 調用 mixin 的方法 }, tick: function() { this.setState({seconds: this.state.seconds + 1}); }, render: function() { return ( <p>React 已經運行了 {this.state.seconds} 秒。</p> ); } }); React.render( <TickTock />, document.getElementById('example'));
函數原型 object statics
statics對象容許你定義靜態的方法,這些靜態的方法能夠在組件類上調用。例如:
var MyComponent = React.createClass({ statics: { customMethod: function(foo) { return foo === 'bar'; } }, render: function() { } }); MyComponent.customMethod('bar'); // true
在這塊定義的方法都是靜態的,意味着你能夠在任何組件實例建立以前調用它們,這些方法不能獲取組件的props和state。若是你想在靜態方法中檢查props的值,在調用處把props做爲參數傳入到靜態方法。
函數原型 string displayName
displayName 描述插件的顯示名稱。JSX自動設置該值。
var App = React.createClass({ displayName: 'App', render: function () { return ( <h1>itbilu.com</h1> ) } });
所謂生命週期,就是一個對象從開始生成到最後消亡所經歷的狀態某個肯定的時間點執行的方法,理解生命週期,是合理開發的關鍵。經過反覆試驗,獲得了組件的生命週期在不一樣狀態下的執行順序:
1.當首次裝載組件時: 按順序執行 getDefaultProps、getInitialState、componentWillMount、render 和 componentDidMount;
2.當從新裝載組件時, 此時按順序執行 getInitialState、componentWillMount、render 和componentDidMount,並不執 行getDefaultProps
3.當再次渲染組件時,組件接受到新的Props,此時按順序執行componentWillReceiveProps、shouldComponentUpdate、
componentWillUpdate、render 和componentDidUpdate。
4.當再次渲染組件時,組件接受到新的state,此時按順序執行shouldComponentUpdate、componentWillUpdate、render 和componentDidUpdate。
4.當組件卸載時: 執行componentWillUnmount
如圖,能夠把組件生命週期大體分爲三個階段:
第一階段:是組件第一次繪製階段,如圖中的上面虛線框內,在這裏完成了組件的加載和初始化;
第二階段:是組件在運行和交互階段,如圖中左下角虛線框,這個階段組件能夠處理用戶交互,或者接收事件更新界面;
第三階段:是組件卸載消亡的階段,如圖中右下角的虛線框中,這裏作一些組件的清理工做。
下圖對每種狀況作了更清晰明瞭的說明
生命週期回調函數
下面來詳細介紹生命週期中的各回調函數。
getDefaultProps
其原型以下:
object getDefaultProps();
在組件類建立的時候調用一次,而後返回值被緩存下來。若是父組件沒有指定 props 中的某個鍵,則此處返回的對象中的相應屬性將會合併到this.props(
使用in
檢測屬性).該方法在任何實例建立以前調用,所以不能依賴於this.props
。另外,getDefaultProps()
返回的任何複雜對象將會在實例間共享,而不是每一個實例擁有一份拷貝。
getInitialState
其原型以下:
object getInitialState()
在組件掛載以前調用一次。返回值將會做爲 this.state
的初始值。
componentWillMount
而後,準備加載組件,會調用 componentWillMount(),其原型以下:
void componentWillMount()
這個函數調用時機是在組件建立,並初始化了狀態以後,在第一次繪製 render() 以前。能夠在這裏作一些業務初始化操做,也能夠設置組件狀態。這個函數在整個生命週期中只被調用一次。
componentDidMount
在組件第一次繪製以後,會調用 componentDidMount(),函數原型以下:
void componentDidMount()
通知組件已經加載完成。這個函數調用的時候,真實DOM已經構建完成,你能夠在這個函數開始獲取其中的元素或者子組件了。須要注意的是,React框架是先調用子組件的 componentDidMount(),而後調用父組件的componentDidMount函數。從這個函數開始,HTML就能夠和JS交互了,例如設置計時setTimeout或者setInterval,或者發起網絡請求。這個函數也是隻被調用一次。這個函數以後,就進入了穩定運行狀態,等待事件觸發。
componentWillReceiveProps
若是組件收到新的屬性(props),就會調用 componentWillReceiveProps(),其原型以下:
void componentWillReceiveProps(object nextProps)
輸入參數 nextProps 是即將被設置的屬性,舊的屬性仍是能夠經過 this.props 來獲取。在這個回調函數裏面,你能夠根據屬性的變化,經過調用 this.setState() 來更新你的組件狀態,這裏調用更新狀態是安全的,並不會觸發額外的 render() 調用。以下:
componentWillReceiveProps:function(nextProps) {
this.setState({
likesIncreasing: nextProps.likeCount >this.props.likeCount
});
}
shouldComponentUpdate
當組件接收到新的屬性和狀態改變的話,都會觸發調用 shouldComponentUpdate(...),函數原型以下:
boolean shouldComponentUpdate(object nextProps, object nextState)
輸入參數nextProps和上面的componentWillReceiveProps函數同樣,nextState表示組件即將更新的狀態值。這個函數的返回值決定是否須要更新組件,若是true表示須要更新,繼續走後面的更新流程。否者,則不更新,直接進入等待狀態。默認狀況下,這個函數永遠返回true用來保證數據變化的時候UI可以同步更新。在大型項目中,你能夠本身重載這個函數,經過檢查變化先後屬性和狀態,來決定UI是否須要更新,能有效提升應用性能。
componentWillUpdate
若是組件狀態或者屬性改變,而且上面的shouldComponentUpdate返回爲true,就會開始準更新組件,並調用componentWillUpdate(),其函數原型以下:
void componentWillUpdate(object nextProps, object nextState)
輸入參數與shouldComponentUpdate同樣,在這個回調中,能夠作一些在更新界面以前要作的事情。須要特別注意的是,在這個函數裏面,你就不能使用this.setState來修改狀態。這個函數調用以後,就會把nextProps和nextState分別設置到this.props和this.state中。緊接着這個函數,就會調用render()來更新界面了。
componentDidUpdate
調用了render()更新完成界面以後,會調用componentDidUpdate()來獲得通知,其函數原型以下:
void componentDidUpdate(object prevProps, object prevState)
由於到這裏已經完成了屬性和狀態的更新了,此函數的輸入參數變成了prevProps和prevState。
componentWillUnmount
當組件要被從界面上移除的時候,就會調用componentWillUnmount(),其函數原型以下:
void componentWillUnmount()
在這個函數中,能夠作一些組件相關的清理工做,例如取消計時器、網絡請求等。
總結:
1.react組件的方法和屬性
render
displayName
mixins
propTypes
statics
getDefaultProps
getInitialState
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
2.編程建議
1. 不建議在 getDefaultProps、getInitialState、shouldComponentUpdate、componentWillUpdate、render,componentWillUnmount
中調用setState,特別注意:不能在shouldComponentUpdate 和 componentWillUpdate中調用setState,由於更新 state 會致使組件更新進而調用shouldComponentUpdate和componentWillUpdate方法,在shouldComponentUpdate和componentWillUpdate中更新了state,又會致使組件更新而調用shouldComponentUpdate和componentWillUpdate方法,簡而言之,會致使循環調用。
2.除了須要操做DOM的初始化操做放在componentDidMount中,其他的初始化操做應所有丟到componentWillMount中進行,特別是涉及修改state 的操做。由於這些方法若是丟到componentDidMount中,會致使組件加載完成後馬上檢測到state變動,觸發組件再次更新,影響頁面性能。
3.不要在getDefaultProps中進行任何針對實例的操做。該方法在任何實例建立前調用,在該方法中訪問不到任何實例。