render
裏面儘可能減小新建變量和bind
函數的使用,儘可能減小傳遞參數的數量在render
中綁定函數,無非就是下面三種:javascript
render() { return ( <div className='app'> <span onClick={this.handleClick}>1</span> <span onClick={this.handleClick.bind(this)}>2</span> <span onClick={()=>this.handleClick()}>3</span> </div> ) }
第一種是在構造函數中綁定this
,第二種是在render()
函數裏面綁定this
,第三種就是使用箭頭函數,上述方法都能實現this
的綁定。java
可是哪種方法的性能最好,是咱們要考慮的問題。毫無疑問第一種的性能最好。react
第一種方法,構造函數每渲染一次便會執行一遍;算法
第二種方法,在每次render()
的時候都會從新執行一遍函數;編程
第三種方法,每一次render()
的時候,都會生成一個新的箭頭函數,即便兩個箭頭函數的內容是同樣的。數組
react
判斷是否須要進行render
是淺層比較,簡單來講就是經過===
來判斷的,若是state
或者prop
的類型是字符串或者數字,只要值相同,那麼淺層比較就會認爲其相同;瀏覽器
可是若是前者的類型是複雜的對象的時候,咱們知道對象是引用類型,淺層比較只會認爲這兩個prop
是否是同一個引用,若是不是,哪怕這兩個對象中的內容徹底同樣,也會被認爲是兩個不一樣的prop
。安全
舉個例子:性能優化
當咱們給組件App
名爲style
的prop
賦值;數據結構
<App style={{ color:"green" }}
使用這種方法,每一次渲染都會被認爲是一個style
這個prop
發生了變化,由於每一次都會產生一個對象給style
。
若是想要讓react
渲染的時候認爲先後對象類型prop
相同,則必需要保證prop
指向同一個javascript
對象,改進以下:
const appStyle = { color: "red" }; //這個初始化只執行一次,不要放在render中,能夠放在構造函數中 <App style={appStyle} />
shouldComponentUpdate
函數生命週期函數shouldComponentUpdate
是決定react
組件何時可以從新渲染的函數,可是這個函數默認的實現方式就是簡單的返回一個true
。也就是說,默認每次更新的時候都要調用所用的生命週期函數,包括render
函數,從新渲染。
看看下面這個例子:
class App extends React.Component { constructor(props) { super(props); this.state = { count = 2, name = 'apple', } this.handleClick = this.handleClick.bind(this); this.handleName = this.handleName.bind(this); } this.handleClick() { // ... } this.handleName() { // ... } render() { return ( <div className='app'> <span>數量,{this.state.count}</span> <button onClick={this.handleClick}>改變數量</button> <button onClick={this.handleName}>改變名字</button> <Child title={this.state.name}></Child> </div> ); } } class Child extends React.Component { render() { console.log('render了一次'); return ( <h3>我想吃,{this.props.title}</h3> ); } }
咱們寫了兩個組件,App
和Child
組件,並寫兩個方法,一個改變App
中的count
的值,一個是改變name
,咱們在Child
的render
中打印了每次是否執行。
不出意外,雖然Child
組件裏的title
值沒有改變,可是仍是render
了。
爲了進一步優化這個問題,咱們這樣改Child
組件:
class Child extends React.Component { shouldComponentUpdate(nextProps,nextState) { if(nextProps.title == this.props.title) { return false; } return true; } render() { console.log('render了一次'); return ( <h3>我想吃,{this.props.title}</h3> ); } }
只有當Child
的title
值發生改變的時候,組件纔會去render
。
在最新的react
中,react
給咱們提供了React.PureComponent
,官方也在早期提供了名爲react-addons-pure-render-mixin
插件來從新實現shouldComponentUpdate
生命週期方法。
class Child extends React.PureComponent { // shouldComponentUpdate(nextProps,nextState) { // if(nextProps.title == this.props.title) { // return false; // } // return true; // } render() { console.log('render了一次'); return ( <h3>我想吃,{this.props.title}</h3> ); } }
經過上述的方法的效果也是和咱們先前定製shouldComponentUpdate
的效果是一致的。
可是咱們要注意的是,這裏的PureRender
是淺比較的,由於深比較的場景是至關昂貴的。因此咱們要注意咱們在1.1
中說到的一些注意點:不要直接爲props
設置對象或者數組、不要將方法直接綁定在元素上,由於其實函數也是對象。
javascript
中的對象通常都是可變的,由於使用了引用賦值,新的對象簡單的引用了原始對象,改變新對象將影響到原始對象。
舉個例子:
student = { age : 1 }; school = student; school.age = 2;
當咱們給school.age
賦值後,會發現student.a
也變成了2,雖然咱們能夠經過深拷貝與淺拷貝解決這個問題,可是這樣作很是的昂貴,對cpu
和內存會形成浪費。
這裏就須要用到Immutable
,經過Immutable
建立的Immutable Data
一旦被建立,就不能再更改。對Immutable
對象進行修改、添加或刪除操做,都會返回一個新的Immutable
對象。
下面是三個比較重要且用到的數據結構
Map
:鍵值對集合,對應Object,ES6中也有專門的Map對象List
:有序可重複列表,對應於ArrayArraySet
:有序且不可重複的列表咱們能夠看一個例子:
使用Map
生成一個immutable
對象:
import { Map, is } from 'immutable'; let a = Map({ 'name': 'apple', 'list': Map({name: 'orange'}) }) let b = a.set('name','banana'); console.log(a.get('course') === b.get('course')); // 返回true console.log(a === b); // 返回false
Immutable.is
比較的是兩個對象的 hashCode
或 valueOf
(對於JavaScript
對象)。因爲immutable
內部使用了Trie
數據結構來存儲,只要兩個對象的 hashCode
相等,值就是同樣的。這樣的算法避免了深度遍歷比較,性能很是好。
Immutable
優勢:
shouldComponentUpdate
方便Immutable
缺點:
seamless-immutable
)react
組件在裝載過程當中,react
經過在render
方法在內存中產生一個樹形結構,樹上的節點表明一個react
組件或者原生的Dom
元素,這個樹形結構就是咱們所謂的Vitural Dom
,react
根據這個來渲染產生瀏覽器的Dom
樹。
react
在更新階段對比原有的Vitural Dom
和新生成的Vitural Dom
,找出不一樣之處,在根據不一樣來渲染Dom
樹。
react
爲了追求高性能,採用了時間複雜度爲O(N)
來比較兩個屬性結構的區別,由於要確切比較兩個樹形結構,須要經過O(N^3)
,這會下降性能。
// A組件 <div> <Todos /> </div> // B組件 <span> <Todos /> </span>
咱們想把A
組件更新成B
組件,react
在作比較的時候,發現最外面的根結點徹底不同,直接銷燬以前的<div>
節點,包括裏面的子節點也一併銷燬,這是一個巨大的浪費,可是爲了不O(N^3)
的時間複雜度,只能採用這種方式。
因此在開發過程當中,咱們應該儘可能避免上面的狀況,不要將包裹節點的類型隨意改變。
這裏包括兩種狀況,一種是節點是Dom
類型,還有一種react
組件。
對於dom
類型,咱們舉個例子:
// A組件 <div style={{color: 'red',fontSize:15}} className="welcome"> Hello World!!! </div> // B組件 <div style={{color: 'green',fontSize:15}} className="react"> Good Bye!!! </div>
上述A和B組件的區別是文字、className
、style
中的color
發生改變,由於Dom
元素沒變,React
只會修改他變化的部分。
針對react
組件類型,渲染無非就是再執行一遍組件實例的更新過程,最主要的就是定製shouldComponentUpdate
。
例子一:
// A <ul> <TodoItem text="First" complete={false} /> <TodoItem text="Second" complete={false} /> </ul> // B <ul> <TodoItem text="First" complete={false} /> <TodoItem text="Second" complete={false} /> <TodoItem text="Third" complete={false} /> </ul>
從A變到B,若是shouldComponentUpdate
處理得當,咱們只須要更新裝載third
的那一次就行。
咱們來看看下一個例子:
// A <ul> <TodoItem text="First" complete={false} /> <TodoItem text="Second" complete={false} /> </ul> // B <ul> <TodoItem text="Zero" complete={false} /> <TodoItem text="First" complete={false} /> <TodoItem text="Second" complete={false} /> </ul>
這裏由於react
是採用O(n)
的時間複雜度,因此會依次將text
爲First
的改成Zero
,text
爲Second
改成First
,在最後再加上一個組件,text
爲Second
。現存的兩個的text
的屬性都被改變了,因此會依次渲染。
若是咱們這裏有100個實例,那麼就會發生100次更新。
這裏咱們就要用到Key
了
簡單來講,其實這一個Key
就是react
組件的身份證號。
咱們將上一個例子改爲以下,就能夠避免上面的問題了,react
就可以知道其實B裏面的第二個和第三個組件其實就是A中的第一個和第二個實例。
// A <ul> <TodoItem key={1} text="First" complete={false} /> <TodoItem key={2} text="Second" complete={false} /> </ul> // B <ul> <TodoItem key={0} text="Zero" complete={false} /> <TodoItem key={1} text="First" complete={false} /> <TodoItem key={2} text="Second" complete={false} /> </ul>
不過如今,react
也會提醒咱們不要忘記使用key
,若是沒有加,瀏覽器中會報錯。
關於key
的使用咱們要注意的是,這個key
值要穩定不變的,就如同身份證號對於咱們是穩定不變的同樣。
一個常見的錯誤就是,拿數組的的下標值去當作key
,這個是很危險的,代碼以下,咱們必定要避免
<ul> { todos.map((item, index) => { <TodoItem key={index} text={item.text} completed={item.completed} > }) } </ul>
未完待續...