一行代碼賽過千言萬語。。這篇文章呢。。主要講述我一步一步優化react性能的過程。。爲啥要用immutable.js呢。絕不誇張的說。有了immutable.js(固然也有其餘實現庫)。。才能將react的性能發揮到極致!要是各位看官用過一段時間的react,而沒有用immutable那麼本文很是適合你。那麼我開始吧!react
想象一下這種場景,一個父組件下面一大堆子組件。而後呢,這個父組件re-render。是否是下面的子組件都得跟着re-render。但是不少子組件裏面是冤枉的啊!!不少子組件的props 和 state 然而並無改變啊!!雖然virtual dom 的diff 算法很快。。可是性能也不是這麼浪費的啊!!下面咱們上代碼算法
如下是父組件代碼。。負責輸入name 和 age 而後循環顯示name 和 ageredux
export default class extends Component { constructor(props){ super(props) this.state={ name:"", age :"", persons:[] } } render() { const {name,age,persons} = this.state return ( <div> <span>姓名:</span><input value={name} name="name" onChange={this._handleChange.bind(this)}></input> <span>年齡:</span><input value={age} name="age" onChange={this._handleChange.bind(this)}></input> <input type="button" onClick={this._handleClick.bind(this)} value="確認"></input> {persons.map((person,index)=>( <Person key={index} name={person.name} age={person.age}></Person> ))} </div> ) } _handleChange(event){ this.setState({[event.target.name]:event.target.value}) } _handleClick(){ const {name,age} = this.state this.setState({ name:"", age :"", persons:this.state.persons.concat([{name:name,age:age}]) }) } }
如下是子組件代碼單純的顯示name和age而已安全
class Person extends Component { componentWillReceiveProps(newProps){ console.log(`我新的props的name是${newProps.name},age是${newProps.age}。我之前的props的name是${this.props.name},age是${this.props.age}是我要re-render了`); } render() { const {name,age} = this.props; return ( <div> <span>姓名:</span> <span>{name}</span> <span> age:</span> <span>{age}</span> </div> ) } }
運行起來長下圖這個樣
好那麼問題來了。。咱們看一下控制檯
天哪,,這麼屢次re-reder..細細觀看。。不難發現。要re-render這麼屢次。。父組件一re-render,子組件就跟着re-render啊。。那麼多麼浪費性能,好。。PureRenderMixin出場dom
由於咱用的是es2015的 Component,因此已經不支持mixin了。。不過不要緊,能夠用
HOCs,這個比mixin還更受推崇呢。。我有空回用代碼來展現他倆的異同,鑑於不是本文重點,,你們能夠看這兩篇文章瞭解下React Mixin 的前世此生 和Mixins Are Dead. Long Live Composition
因此在這裏咱們用Pure render decorator代替PureRenderMixin,那麼代碼以下函數
import pureRender from "pure-render-decorator" ... @pureRender class Person extends Component { render() { console.log("我re-render了"); const {name,age} = this.props; return ( <div> <span>姓名:</span> <span>{name}</span> <span> age:</span> <span>{age}</span> </div> ) } }
加個這東西就完事了??看上去咋這麼不使人信服啊。。無論怎樣。。試試吧
果真能夠作到pure render。。在必須render 的時候才render
好咱們看看它的神奇之處性能
@pureRender
是es7的Decorators語法。上面這麼寫就和下面這麼寫同樣優化
class PersonOrigin extends Component { render() { console.log("我re-render了"); const {name,age} = this.props; return ( <div> <span>姓名:</span> <span>{name}</span> <span> age:</span> <span>{age}</span> </div> ) } } const Person = pureRender(PersonOrigin)
pureRender其實就是一個函數,接受一個Component。把這個Component搞一搞,返回一個Component
看他pureRender的源代碼就一目瞭然this
function shouldComponentUpdate(nextProps, nextState) { return shallowCompare(this, nextProps, nextState); } function pureRende(component) { component.prototype.shouldComponentUpdate = shouldComponentUpdate; } module.exports = pureRender;
pureRender很簡單,就是把傳進來的component的shouldComponentUpdate給重寫掉了,原來的shouldComponentUpdate,不管怎樣都是return ture,如今不了,我要用shallowCompare比一比,shallowCompare代碼及其簡單,以下spa
function shallowCompare(instance, nextProps, nextState) { return !shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState); }
一目瞭然。分別拿如今props&state和要傳進來的props&state,用shallowEqual比一比,要是props&state都同樣的話,就return false,是否是感受很完美?不。。這纔剛剛開始,問題就出在shallowEqual上了
不少時候,父組件向子組件傳props的時候,可能會傳一個複雜類型,好比咱們改下。
render() { const {name,age,persons} = this.state return ( <div> ...省略..... {persons.map((person,index)=>( <Person key={index} detail={person}></Person> ))} </div> ) }
person是一個複雜類型。。這就埋下了隱患,,在演示隱患前,咱們先說說shallowEqual,是個什麼東西,shallowEqual其實只比較props的第一層子屬性是否是相同,就像上述代碼,props 是以下
{ detail:{ name:"123", age:"123"} }
他只會比較props.detail ===nextProps.detail
那麼問題來了,上代碼
若是我想修改detail的時候考慮兩種狀況
這樣就會引發一個bug,好比我修改detail.name,由於detail的引用沒有改,因此
props.detail ===nextProps.detail 仍是爲true。。
因此咱們爲了安全起見必須修改detail的引用,(redux的reducer就是這麼作的)
這種雖然沒有bug,可是容易誤殺,好比若是我新舊兩個detail的內容是同樣的,豈不是還要,render。。因此仍是不完美,,你可能會說用 深比較就行了,,可是 深比較及其消耗性能,要用遞歸保證每一個子元素同樣,
這只是說沒有用immutable引發各類?。。下一篇我講寫,,如何用immutable.js..