阅读下面的代码,请回答 render 总共执行了多少次?这个问题涉及到react的生命周期。
代码如下
import React, { Component, PureComponent } from 'react'
/**
* 组件有两个状态,一个是li,另一个是添加li
*/
interface ITestPureComponentS {
lis: number[],
addLi: (li: number) => void
}
// 测试li 组件
class TestLi extends Component {
render() {
console.log('TestLi render');
return (
<li>{this.props.children}</li>
)
}
}
export default class TestPureComponent extends Component<{}, ITestPureComponentS> {
state = {
lis: [],
addLi: (li: number) => {
this.setState({
lis: [...this.state.lis, li]
})
}
}
// 组件即将渲染的时候,添加10个li
componentDidMount() {
let lis: number[] = []
for (let i = 1; i <= 10; i++) {
lis.push(i)
}
this.setState({
lis: lis
})
}
/**
* 添加一个li
*/
addLis = () => {
const num = Date.now()
this.state.addLi(num)
}
render() {
console.log('TestPureComponent render');
const lisD = this.state.lis.map(i => {
return (
<TestLi key={i}>{i}</TestLi>
)
})
return (
<div>
{lisD}
<button onClick={this.addLis}>添加一个li</button>
</div>
)
}
}
分析
app 组件
: 这里只是直接挂载,没有做任何操作,所以render 只会执行一次
TestPureComponent
组件: 组件挂载阶段, 是需要执行一次的,然后组件挂载完毕后,我们手动setState({})
,调用这个方法是会触发组件的render()函数的。 然后它还要在执行一次,所以这个组件的render() 总共执行两次
TestLi
组件: 在父组件被render的同时子组件也会调用render(), 我们这里是直接使用 this.state.lis 的,一开始是空的,里面是没有TestLi
组件的. 所以该组件执行的次数 应该为10 次
打印结果
如果可以答对的网友,恭喜你,对react 的生命周期还是蛮清楚的。加油,继续往下
点击,添加一个li render 又会执行多少次?
分析
TestPureComponent
组件: 点击按钮会触发 this.setState() 所以,TestPureComponent 组件必然会执行 render() ,该组件会执行 render1次
TestLi
组件 由于父组件执行render 并且数据发生变化,里面的值多了一个,所以改组件会执行11 次
效果
注意: 我们只是加了一个数据,并且数据都没有变化,为啥要渲染11次呀,这个render 虽然只是一点点的性能,如果组件树大了,那么render的次数也会相应的变得庞大。这个是非常影响性能的。所以有没有啥优化方法?
优化方法一
: 代码结构优化,我们不在render()这里 进行map遍历,而是直接在componentDidMount的时候直接生成一个TestLi
组件的数组,然后改变数据就不会重新渲染了, 如果网友你能想到这个问题?那确实你react 方面掌握的是非常厉害的。本人是刚刚误打误撞发现的。但是在大部分同学写代码都不怎么能优化,所以选择优化方法二;
优化方法二
:shouldComponentUpdate(nextProps, nextState)
这里面能够直接决定是否需要重新 执行 render, 所以优化的方法就在这个路口,优化的思路也是比较容易的,将传递过来的state 和 props 和当前的 state, props进行比较,如果变化了,那么直接return true, 否则 return false;
在TestLi组件中进行优化
// 测试li 组件
class TestLi extends Component {
/**
* 浅判断
* @param obj
* @param obj2
* @returns
*/
objectIsEqual(obj: any, obj2: any) {
for (const key in obj) {
if (Object.is(obj[key], obj2[key])) {
return true;
}
}
return false
}
shouldComponentUpdate(nextProps: {}, nextState: {}) {
if (this.objectIsEqual(nextProps, this.props)) {
return false;
}
return true
}
render() {
console.log('TestLi render');
return (
<li>{this.props.children}</li>
)
}
}
效果
注意: 有的网友又要问了,为啥要使用
浅比较
而不进行深比较,个人决定浅比较不怎么消耗内存,省时省力。如果深度比较的话,那么就会比较消耗内存了,并且我们是在做优化操作,不需要进行深度比较。
PureComponent
PureComponent 就是上面TaskLi
里面封装的一个组件,改组件继承component
, 里面的实现思路也是一样的,都是在shouldComponentUpdate
这里进行操作的,也是使用的是浅比较
代码改写成这样,也是可以实现一样的效果
// 测试li 组件
class TestLi extends PureComponent {
render() {
console.log('TestLi render');
return (
<li>{this.props.children}</li>
)
}
}
注意
- pureComponent 是纯组件,进行的是浅比较,因此数据如果涉及到引用类型的,如对象或者数组等,需要重新创建一个,不然是无法进行数据更新的。为了效率,我们应该使用这个组件
- 要求不改变之前的状态,需要对状态进行覆盖(Immutable)这个公共库可以避免我们犯错,具体的请自行查看。
- 如果函数组件需要使用纯组件,需要使用 React.memo 制作村组件,这个memo就是一个高阶组件,里面返回的还是一个类组件。有兴趣的自己编写