前端爱好者的知识盛宴
React因为他的性能而著名。因为他有一个虚拟DOM层并且只有在需要时才更新真实DOM。即使是同样地信息这也比一直直接更新DOM要快很多。但是,React的智能仅此而已(目前为止),我们的任务是知道React的预期行为以及限制,这样我们才不会意外损失性能。
我们需要关注的一方面是React如何决定什么时候重新渲染组件。不是重新渲染DOM节点,只是调用render方法来改变虚拟DOM。我们可以通过告诉React什么时候需要渲染什么时候不需要渲染来帮助React。让我们依次来看看这些。
1. 组件的状态发生改变
只有在组件的state变化时才会出发组件的重新渲染。状态的改变可以因为props的改变,或者直接通过setState方法改变。组件获得新的状态然后React决定是否应该重新渲染组件。不幸的是,React难以置信简单地将默认行为设计为每次都重新渲染。
组件改变?重新渲染。父组件改变?重新渲染。一部分没有导致视图改变的props改变?重新渲染。
在这个(非常刻意的)例子中,Todo将会每秒重新渲染依次,即使render方法根本没有使用unseen。事实上,unseen值甚至都不改变。你可以在CodePen点击预览里查看这个例子的实际版本。
好吧,但是每次都重新渲染没有什么帮助。
我的意思是,我非常感谢React的细心谨慎。如果状态改变但是组件没有正确渲染的话更糟。权衡之下,每次都重新渲染绝对是一个安全的选择。
但是重新渲染的时间成本看起来非常昂贵(例子里非常夸张地表现了出来)。
是的,在不必要的时候重新渲染会浪费循环并且不是一个好的想好。但是,React不能知道什么时候可以安全的跳过重新渲染,所以React无论是否重要每次都重新渲染。
我们如何告诉React跳过重新渲染?
那就是第二点要说的内容。
2. shouldComponentUpdate方法
shouldComponentUpdate方法默认返回true,这就是导致每次更新都重新渲染的原因。但是你可以在需要优化性能时重写这个方法来让React更智能。比起让React每次都重新渲染,你可以告诉React你什么时候不像触发重新渲染。
当React将要渲染组件时他会执行shouldComponentUpdate方法来看它是否返回true(组件应该更新,也就是重新渲染)。所以你需要重写shouldComponentUpdate方法让它根据情况返回true或者false来告诉React什么时候重新渲染什么时候跳过重新渲染。
当你使用shouldComponentUpdate方法你需要考虑哪些数据对与重新渲染重要。让我们回到这个例子。
正如你所看到的,我们只想在title和done属性改变的时候重新渲染Todo。我们不关心unseen是否改变,所以我没有把它包含在shouldComponentUpdate方法中。
当React渲染Todo组件(通过setState触发)他会首先检查状态是否改变(通过props和state)。假设状态改变了(因为我们显式地调用了setState所以这会发生)React会检查Todo的shouldComponentUpdate方法。React会根据shouldComponentUpdate方法返回值为true或者false来决定从哪里渲染。
更新后的代码仍然会每秒调用一次setState但是render只有在第一次加载时(或者title或done属性改变后)才会调用。你可以在这里点击预览看到。
看起来有很多工作去做。
是的,这个例子非常冗长因为有两个属性(title和done)需要关注并且只有一个可以忽略(unseen)。根据你的数据可能仅检查一个或两个属性并且忽略其他会更有意义。
重要提示
当子组件的的state变化时, 返回false并不能阻止它们重渲染。
这作用于子组件的状态而不是他们的props。所以如果一个子组件内部管理了一些他自己的状态(使用他自己的setState),这仍然会更新。但是如果父组件的shouldComponentUpdate方法返回了false就不会传递更新后的props给他的子组件,所以子组件不会重渲染,即使他们的props变化了。
额外内容:简单性能测试
编写并且在shouldComponentUpdate方法中运行计算的时间成本可能会很昂贵,所以你需要确保值得做。在写shouldComponentUpdate方法前你可以测试React一个周期默认会消耗多少时间。有了这个信息做参考,在做性能优化时你可以做一个不盲目的决定。
使用React的性能工具去发现浪费的周期:
哪一个组件浪费了很多渲染周期?你怎么通过shouldComponentUpdate方法让他们更智能?试着使用性能测试工具来比较他们的性能。