刚开始写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