React16的memo和PureComponent作用类似,只不过前者只适用于函数组件,后者适应于类组件。
关于两者的作用,我们先从PureComponent说起,在了解PureComponent之前要先了解shouldComponentUpdate函数,在类组件中有一个叫做shouldComponentUpdate的生命周期函数,这个函数可以通过返回true或false来决定当前组件是否重新渲染。
而PureComponent自带shouldComponentUpdate函数,PureComponent自带的shouldComponentUpdate函数会在当前组件的props或者state发生变化时,对props或者state进行浅对比,如果相同则不进行重新渲染,反之则会进行渲染。
说到这里来了解一下浅对比,啥叫浅对比呢,类似于浅拷贝,具体的内容大家可以去百度,这里不做赘述,只做简单说明,举个例子,看代码:
import React,{memo} from "react";
import ReactDOM from 'react-dom';
class CommentList extends React.Component {
constructor(props) {
super(props);
this.state = {
commentList: [{
body: '大三岁', author: '张三'
}, {
body: '新冠病毒', author: '李四'
}]
}
}
componentWillMount() {
setTimeout(() => {
this.setState((prevState, props) => {
return {
commentList: [{
body: '大三岁', author: '张三'
}, {
body: '新冠1病毒', author: '李四'
}]
}
})
}, 1000)
}
render() {
return (
<div>
{
this.state.commentList.map((item, index) => {
return <Comment {...item} key={index} />
})
}
</div>
)
}
}
function Comment(props) {
console.log('渲染了', props)
return <div>
<p>{props.body}</p>
<p>{props.author}</p>
<p>{props.arr.length}</p>
</div>
}
阅读源码,我们定义了两个组件,CommentList组件与Comment组件,CommentList组件内部的state是一个数组,每条数据用Comment来渲染。我们在componentWillMount设置定时器模拟数据发生变化,我们看到第二条数据的body发生变化了,所以对应的comment组件应该重新渲染,但是我们发现所有Comment组件都重新渲染了。这不利于我们页面的性能优化,咋办呢?
此时我们可以借助memo或者PureComponent来实现只让该渲染的组件渲染,啥是该渲染的组件呢?那就是props或者state发生变化的组件,那变化的标准是怎么样的呢?
来看代码,用memo来实现:
import React,{memo} from "react";
import ReactDOM from 'react-dom';
class CommentList extends React.Component {
constructor(props) {
super(props);
this.state = {
commentList: [{
body: '大三岁', author: '张三'
}, {
body: '新冠病毒', author: '李四'
}]
}
}
componentWillMount() {
setTimeout(() => {
this.setState((prevState, props) => {
return {
commentList: [{
body: '大三岁', author: '张三'
}, {
body: '新冠1病毒', author: '李四'
}]
}
})
}, 1000)
}
render() {
return (
<div>
{
this.state.commentList.map((item, index) => {
return <Comment {...item} key={index} />
})
}
</div>
)
}
}
function Comment(props) {
console.log('渲染了', props)
return <div>
<p>{props.body}</p>
<p>{props.author}</p>
<p>{props.arr.length}</p>
</div>
}
// 第一种方式 用memo包裹函数组件
Comment = memo(Comment)
用PureComponent来实现:
import React,{memo} from "react";
import ReactDOM from 'react-dom';
class CommentList extends React.Component {
constructor(props) {
super(props);
this.state = {
commentList: [{
body: '大三岁', author: '张三'
}, {
body: '新冠病毒', author: '李四'
}]
}
}
componentWillMount() {
setTimeout(() => {
this.setState((prevState, props) => {
return {
commentList: [{
body: '大三岁', author: '张三'
}, {
body: '新冠1病毒', author: '李四'
}]
}
})
}, 1000)
}
render() {
return (
<div>
{
this.state.commentList.map((item, index) => {
return <Comment {...item} key={index} />
})
}
</div>
)
}
}
class Comment extends React.PureComponent {
constructor() {
super()
}
render() {
console.log('渲染了')
const props = this.props
return <div>
<p>{props.body}</p>
<p>{props.author}</p>
</div>
}
}
阅读源码,我们分别用memo和PureComponent改造了代码,并且运行的话,只有该渲染的组件发生了渲染。
渲染的组件前后的props发生变化的组件才会重新渲染,来看一下props或者state发生变化的标准:
{body: '新冠病毒', author: '李四'}
{body: '新冠1病毒', author: '李四'}
上面两个对象进行浅对比body发生变化,所以会渲染,而另一个组件数据没变所以没有重新渲染,从而提高了页面性能。
以上便是memo的作用了,但是这里有一个问题,那就是如果props中的某一个属性是引用数据类型,这个引用数据发生改变,但是引用未变,组件是不会重新渲染的,首先我们看下不使用memo的代码:
import React,{memo} from "react";
import ReactDOM from 'react-dom';
class CommentList extends React.Component {
constructor(props) {
super(props);
this.state = {
commentList: [{
body: '大三岁', author: '张三',arr:[]
}, {
body: '新冠病毒', author: '李四',arr:[]
}]
}
}
componentWillMount() {
setTimeout(() => {
this.state.commentList[0].arr.push(999)
this.setState({ commentList: this.state.commentList})
}, 1000)
}
render() {
return (
<div>
{
this.state.commentList.map((item, index) => {
return <Comment {...item} key={index} />
})
}
</div>
)
}
}
//
function Comment(props) {
console.log('渲染了', props)
return <div>
<p>{props.body}</p>
<p>{props.author}</p>
<p>{props.arr.length}</p>
</div>
}
上面的代码,我们没有使用memo,Comment的props发生变化后,所有的Comment组件都重新渲染了,如图:
上图红框处显示了组件重新渲染了,然后我们用memo改造代码:
import React,{memo} from "react";
import ReactDOM from 'react-dom';
class CommentList extends React.Component {
constructor(props) {
super(props);
this.state = {
commentList: [{
body: '大三岁', author: '张三',arr:[]
}, {
body: '新冠病毒', author: '李四',arr:[]
}]
}
}
componentWillMount() {
setTimeout(() => {
this.state.commentList[0].arr.push(999)
this.setState({ commentList: this.state.commentList})
}, 1000)
}
render() {
return (
<div>
{
this.state.commentList.map((item, index) => {
return <Comment {...item} key={index} />
})
}
</div>
)
}
}
//
function Comment(props) {
console.log('渲染了', props)
return <div>
<p>{props.body}</p>
<p>{props.author}</p>
<p>{props.arr.length}</p>
</div>
}
Comment = memo(Comment)
此时看效果:
上图展示显示了,虽然组件的props发生了变化,但是组件为重新渲染。
那这个bug怎么解决呢,很简单,不要直接修改完数据直接使用,而是修改完后深拷贝一下再去调用setState。
所以在使用memo和PureComponent时大家需要特别注意,总结一下:
1、memo和PureComponent的作用提高渲染性能,避免不必要的渲染。
2、memo和PureComponent的原理是通过浅拷贝来实现的。
3、通过案例演示了memo的作用。
4、memo的缺点,当props或state中的属性有引用属性时需要注意,修改完成后进行一下深拷贝在调用setState。
以上便是使用memo的一下注意事项,希望对你有所帮助。
参考:你真的了解浅比较么?
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。