一行代码胜过千言万语。本文主要讲述了逐步优化反应性能的过程。为什么用不可变. js毫不夸张地说。使用不可变的. js(当然,还有其他实现库)。为了最大限度的表现反应过来!如果读者已经使用了一段时间,但不是一成不变的,那么这篇文章非常适合你。那我就开始了!
1.对于react,如果父组件有多个子组件,
想象一下这个场景,在父组件下有很多子组件。然后,父组件重新呈现。以下子组件是否必须跟随重新渲染。但是很多子组件都错了!但是很多子组件的道具和状态都没变!虽然virtual dom的diff算法很快,但是它的性能并没有那么浪费!现在让我们对它进行编码
1).原始代码如下
以下是父组件代码。输入姓名和年龄,然后循环显示
导出默认类扩展Component { constructor(props){ super(props)this . state={ name : ' ',age : ' ',person 3360[]} } render(){ const { name,age,People}=this。状态返回(div span name :/span输入值={ name } name=' name ' on change={ this。_ handlechange。bind(this)}/输入span age :/Span输入值={age} name=' age' onchange={this。_ handlechange。bind(this)}/input input type=' button ' onclick={ this。_ handleclick。bind (this)}值=' confirm '/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 . setstate({ name : ' ',age : ' ',Persons :state . persons . concat([{ name : name,age : age}])}}}以下子组件代码仅显示姓名和年龄
Class person扩展组件{ component twillreceiveprops(new props){ console . log(`我的新道具的名称是${newProps.name},年龄是${newProps.age}。我以前的道具名字是${this.props.name},年龄是${this.props.age}。我要重新渲染`);} render() { const {name,age }=this.props返回(跨度名称:/跨度{名称}/跨度跨度:/跨度跨度{年龄}/跨度/跨度)}}运行如下图所示
好了,问题来了。让我们看看控制台:
天哪,这么多重新设计.仔细看不难发现。要重新渲染这么多次,父组件一重新渲染,子组件就跟着重新渲染。所以这是对性能的浪费,所以PureRenderMixin出现了
2).PureRenderMixin
因为我们用的是es2015 Component,所以不再支持mixin,不过没关系,我们可以用HOCs,比mixin更受尊重。我有时间重用代码来展示它们之间的异同。由于不是本文的重点,所以可以通过阅读这两篇文章来了解React Mixin和Mixin已死的前世。作文万岁
所以这里我们使用纯渲染装饰器而不是纯渲染混合,所以代码如下
从“纯渲染装饰器”导入纯渲染.@ purerenderclass person扩展了组件{ render(){ console . log(' I re-render ');const {name,age }=this.props返回(span name :/span span { name }/span span age :/span span { age }/span/div)} }添加这个东西就完事了?为什么看起来这么没有说服力?不管怎样,试试吧。
果然,你可以做纯渲染,而且只能在不得已的时候渲染。
好,让我们看看它的魔力
@pureRender
是es7的Decorators语法。上面是这样说的,就像下面一样
Class person origin扩展组件{ render(){ console . log(' I re-render ');const {name,age }=this.propsreturn(span name :/span span { name }/span span age :/span span { age }/span/div)} } const person=pure render(person origin)pure render实际上是一个函数,接受一个Component。弄乱这个组件,返回一个组件,一眼就能看到他的pureRender的源代码
函数should component update(nextProps,nextState) { return浅层比较(this,nextProps,nextState);} function pure rende(component){ component . prototype . should component update=should component update;} module.exports=pureRenderPureRender非常简单,即它重写了传入组件的ShouldcomponentUpdate。最初的shouldComponentUpdate无论如何都是返回true,但是现在我将它与浅层比较进行比较。浅层比较代码非常简单,如下所示
函数浅层比较(instance,nextProps,nextState) { return!浅层平等(instance.props,nextProps) ||!浅层平等(instance.state,next state);}一目了然。比较当前状态和即将到来的状态。如果所有属性都相同,则返回false。感觉完美吗?没有。这才刚刚开始,问题就出在浅层平等
3).肤浅的平等问题
很多时候,当一个父组件传递道具给一个子组件时,它可能会传递一个复杂的类型,比如我们改变它。
Render () {const {name,age,persons}=this。状态返回(div.省略.{人。地图((person,index)=(person key={ index } detail={ person }/person))}/div)}人就是人这就埋下了隐患。在演示隐患之前,我们先来说说什么是浅显平等。浅相等其实只是比较道具的第一个子属性是否相同。就像上面的代码一样,道具如下
{detail: {name:' 123 ',age:' 123'}}他只能比较道具。detail==nextprops。细节,那么问题来了。如果我想修改细节,我应该考虑两种情况。
在第一种情况下,我修改了细节的内容,而没有改变细节的引用
这会导致错误。比如我修改了detail.name,因为细节的引用没有变,所以props . detail===next props . detail还是真的。因此,为了安全起见,我们必须修改细节引用(redux的reducer就是这么做的)
在案例二中,我修改了细节参考
虽然没有bug,但是很容易被误杀。例如,如果我的新旧细节内容相同,为什么不渲染呢?因此,它仍然不是完美的。你可能会说深度比较好,但是深度比较及其消耗性能应该用递归来保证。
这只是说明我没有用不可变来造成各种各样的事情。接下来,我将讨论如何使用不可变的. j
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。