剛開始寫react可能只是寫出來完成業務就完了,後期審查代碼發現可能不少地方其實均可以優化,以前可能有些地方似是而非,在此小結一下。html
react引入了一個叫作虛擬DOM的概念,安插在JavaScript邏輯和實際的DOM之間。這一律念提升了Web性能。在UI渲染過程當中,React經過在虛擬DOM中的微操做來實對現實際DOM的局部更新。react
在Web開發中,咱們總須要將變化的數據實時反應到UI上,這時就須要對DOM進行操做。而複雜或頻繁的DOM操做一般是性能瓶頸產生的緣由,React爲此引入了虛擬DOM(Virtual DOM)的機制:在瀏覽器端用Javascript實現了一套DOM API。基於React進行開發時全部的DOM構造都是經過虛擬DOM進行,每當數據變化時,React都會從新構建整個DOM樹,而後React將當前整個DOM樹和上一次的DOM樹進行對比,獲得DOM結構的區別,而後僅僅將須要變化的部分進行實際的瀏覽器DOM更新。並且React可以批處理虛擬DOM的刷新,在一個事件循環(Event Loop)內的兩次數據變化會被合併,例如你連續的先將節點內容從A變成B,而後又從B變成A,React會認爲UI不發生任何變化,而若是經過手動控制,這種邏輯一般是極其複雜的。儘管每一次都須要構造完整的虛擬DOM樹,可是由於虛擬DOM是內存數據,性能是極高的,而對實際DOM進行操做的僅僅是Diff部分,於是能達到提升性能的目的。這樣,在保證性能的同時,開發者將再也不須要關注某個數據的變化如何更新到一個或多個具體的DOM元素,而只須要關心在任意一個數據狀態下,整個界面是如何Render的。chrome
react的組件渲染分爲初始化渲染和更新渲染。redux
在開發模式下, 在支持的瀏覽器內使用性能工具能夠直觀的瞭解組件什麼時候掛載,更新和卸載小程序
綁定this的方式:通常有下面幾種方式api
constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); //構造函數中綁定 } //而後能夠 <p onClick={this.handleClick}>
<p onClick={this.handleClick.bind(this)}>
<p onClick={() => { this.handleClick() }}>
第三種方法的話,每一次render()的時候,都會生成一個新的箭頭函數數組
shouldComponentUpdate是決定react組件何時可以不從新渲染的函數,返回true時更新,false時不更新。默認返回true,即每次從新渲染,所以咱們能夠重寫個函數從而達到"個性化定製更新"的效果。瀏覽器
class Title extends React.Component { constructor(props) { super(props) } render() { console.log('title render') return ( <div>{this.props.title}</div> ) } } class PureCom extends React.Component { constructor(props) { super(props) this.state = { title: 'pure', num: 0 } this.add = this.add.bind(this); } add() { let { num } = this.state; num++; this.setState({ num }) } render() { console.log('pure render') return ( <div> <Title title={this.state.title} /> <p>{this.state.num}</p> <button onClick={this.add}>add</button> </div> ) } }
class Title extends React.Component { constructor(props) { super(props) } shouldComponentUpdate(nextProps, nextState) { if (nextProps.title != this.props.title) { return true //只有title變化時才更新 } else { return false } } render() { console.log('title render') return ( <div>{this.props.title}</div> ) } }
如今就對了,點擊父組件的add按鈕並無觸發Title組件的更新。數據結構
相似上面的狀況其實咱們常常遇到,所以react提供了PureComponent來解決相似的問題,可讓咱們少寫許多的shouldComponentUpdate。less
class Title extends React.PureComponent { constructor(props) { super(props) } render() { console.log('title render') return ( <div>{this.props.title}</div> ) } }
if (this._compositeType === CompositeTypes.PureClass) { shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState); }
大多數狀況PureComponent均可以解決,可是以前也說過,他是「淺比較」,若是遇到數據結構比較複雜,他是沒法識別的。
class PureCom extends PureComponent { constructor(props) { super(props) this.state = { items: [1, 2, 3], title: 'pure', } this.add = this.add.bind(this); } add() { let { items } = this.state; items.push(23); this.setState({ items }) } render() { console.log('pure render') return ( <div> <Title title={this.state.title} /> <ul> {this.state.items.map((e, i) => { return <li key={i}>{e}</li> })} </ul> <button onClick={this.add}>add</button> </div> ) } }
items
實際上是和state裏面的items
指向相同引用。原理和下面同樣。let a={val:1}; let b=a; b.val=2; console.log(a)//{val:2} console.log(b)//{val:2}
add() { let items =JSON.parse(JSON.stringify(this.state.items));//黑科技 //或者let items=deepCopy(this.state.items); items.push(23); this.setState({ items }) }
add() { let { items } = this.state; items=items.concat(23) //此時的items是一個新數組 this.setState({ items }) }
add() { let { items } = this.state; items = update(items, { $push: [23] }); this.setState({ items }) }
若是你數據比較複雜,可能Immutable會是最好的選擇。官方推薦::seamless-immutable 和immutability-helper。
我的感受redux的渲染機制也是和PureComponent相似的,都是淺比較,所以上面的3種解決辦法也適用於redux.
一些生命週期會被刪除,將在17.0:刪除componentWillMount,componentWillReceiveProps和componentWillUpdate。
componentWillMount
=> componentDidMount
componentWillReceiveProps
=> getDerivedStateFromProps
componentWillUpdate
=> getSnapshotBeforeUpdate
//代替componentWillReceiveProps,由於是靜態方法,不能訪問到 this,避免了一些可能有反作用的邏輯,好比訪問 DOM 等等 //會在第一次掛載和重繪的時候都會調用到,所以你基本不用在constructor里根據傳入的props來setState static getDerivedStateFromProps(nextProps, prevState) { console.log(nextProps, prevState) if (prevState.music !== nextProps.music) { return { music: nextProps.music, music_file: music_file, index:prevState.index+1 }; //document.getElementById('PLAYER').load(); //這裏不對,應該放在getSnapshotBeforeUpdate 和 componentDidUpdate } return null; } getSnapshotBeforeUpdate(prevProps, prevState) { if (this.state.music != prevState.music) { //進行aduio的重載 return true } return null; } componentDidUpdate(prevProps, prevState, snapshot) { if (snapshot !== null) { document.getElementById('PLAYER').load(); //重載 } }
//新的getSnapshotBeforeUpdate生命週期在更新以前被調用(例如,在DOM被更新以前)。今生命週期的返回值將做爲第三個參數傳遞給componentDidUpdate。 (這個生命週期不是常常須要的,但能夠用於在恢復期間手動保存滾動位置的狀況。) class ScrollingList extends React.Component { constructor(props) { super(props); this.listRef = React.createRef(); } getSnapshotBeforeUpdate(prevProps, prevState) { // Are we adding new items to the list? // Capture the scroll position so we can adjust scroll later. if (prevProps.list.length < this.props.list.length) { const list = this.listRef.current; return list.scrollHeight - list.scrollTop; } return null; } componentDidUpdate(prevProps, prevState, snapshot) { //snapshot // If we have a snapshot value, we've just added new items. // Adjust scroll so these new items don't push the old ones out of view. // (snapshot here is the value returned from getSnapshotBeforeUpdate) if (snapshot !== null) { const list = this.listRef.current; list.scrollTop = list.scrollHeight - snapshot; } } render() { return ( <div ref={this.listRef}>{/* ...contents... */}</div> ); } }
//有一個常見的錯誤觀念認爲,在componentWillMount中提取能夠避免第一個空的渲染。在實踐中,這歷來都不是真的,由於React老是在componentWillMount以後當即執行渲染。若是數據在componentWillMount觸發的時間內不可用,則不管你在哪裏提取數據,第一個渲染仍將顯示加載狀態。 // After class ExampleComponent extends React.Component { state = { externalData: null, }; componentDidMount() { this._asyncRequest = asyncLoadData().then( externalData => { this._asyncRequest = null; this.setState({ externalData }); } ); } componentWillUnmount() { if (this._asyncRequest) { this._asyncRequest.cancel(); } } render() { if (this.state.externalData === null) { // Render loading state ... } else { // Render real UI ... } } }
你們好,這裏是「 TaoLand 」,這個博客主要用於記錄一個菜鳥程序猿的Growth之路。這也是本身第一次作博客,但願和你們多多交流,一塊兒成長!文章將會在下列地址同步更新……
我的博客:www.yangyuetao.cn
小程序:TaoLand