你可能没有关注过的 React 性能优化

import React from ‘react’;

class Test extends React.Component {

componentDidUpdate() {

console.log(‘Test componentDidUpdate’);

}

render() {

return 

;

}

}

export default class App extends React.Component {

constructor(props) {

super(props);

this.state = { count: 0 };

this.handleClick = this.handleClick.bind(this);

}

handleClick() {

this.setState((state) => ({

count: state.count + 1,

}));

}

handleTestClick() {}

render() {

return (

{this.state.count}
click

);

}

}

这代码没什么好说的,每次点击click更新state,我现在问几个问题,你先思考一下~

  1. 每次点击click的时候,Test组件会打印Test componentDidUpdate吗?

  2. 如果我把Test组件的React.Component替换为React.PureComponent,结果与上面一样吗?如果不一样,为什么?

  3. 如果我修改这一行代码<Test onClick={this.handleTestClick} /><Test onClick={() => {}} />结果又如何?

shouldComponentUpdate


好像所有的内容都要从这个东西说起,shouldComponentUpdate作为React生命周期的一部分,大多数React开发者至少还是听说过它的,简单来说在这个函数中返回一个布尔值,React会根据这个布尔值来判断组件是否需要重新渲染。

shouldComponentUpdate接收两个参数,一个是更新后的props,一个是更新后的state,可以通过比较两个propsstate来决定是否需要重新渲染组件。

import React from ‘react’;

class Test extends React.Component {

componentDidUpdate() {

console.log(‘Test componentDidUpdate’);

}

// 每次点击 click 都会打印 Test componentDidUpdate

// 添加这个函数后当 count 没有变化时不会打印 Test componentDidUpdate

shouldComponentUpdate(nextProps) {

if (this.props.count === nextProps.count) {

return false;

}

return true;

}

render() {

return 

;

}

}

export default class App extends React.Component {

constructor(props) {

super(props);

this.state = { count: 0 };

this.handleClick = this.handleClick.bind(this);

}

handleClick() {

this.setState((state) => ({

count: state.count,

}));

}

render() {

return (

{this.state.count}
click

);

}

}

这段代码也算比较直观的说明了shouldComponentUpdate的用法,为什么要这么做?当只有一个Test组件的时候可能影响不大,那如果有一千个乃至一万个Test的时候呢,每点击一次click就有一千个、一万个TestcomponentDidUpdate被调用,这就有点夸张了。所以当你在使用循环渲染组件的时候就一定要注意到这一个点,它可能会成为你应用的瓶颈。

现在我们来解一下第一个问题,每次点击click的时候,Test组件会打印Test componentDidUpdate吗?

是的,每次点击click的时候,Test组件会打印Test componentDidUpdate,除非我们在Test中定义了shouldComponentUpdate,同时返回了false阻止其重新渲染。

PureComponent


关于React的这个 API,相信大家也没有那么陌生,根据官方文档的说法ComponentPureComponent很相似,两者的区别在于PureComponent中实现了shouldComponentUpdate函数,这也是为什么我说要从shouldComponentUpdate说起。

import React from ‘react’;

class Test extends React.PureComponent {

componentDidUpdate() {

console.log(‘Test componentDidUpdate’);

}

// 错误的用法

shouldComponentUpdate(nextProps) {

if (this.props.count === nextProps.count) {

return false;

}

return true;

}

render() {

return 

;

}

}

如果你在PureComponent中又使用了shouldComponentUpdate你应该会得到这样一个警告,侧面也告诉我们PureComponent已经实现了shouldComponentUpdate这个函数了。

Test has a method called shouldComponentUpdate(). shouldComponentUpdate should not be used when extending React.PureComponent. Please extend React.Component if shouldComponentUpdate is used.

官网文档中说PureComponent中以浅层对比propsstate的方式来实现了这个函数,也就是浅比较,那什么又是浅比较呢?可以简单的理解为a === b,这里面还是有一些说头的,不过不在本文探讨范围内,举两个例子,大家可以自行搜索理解。

let a = 5;

let b = 5;

let c = {};

let d = {};

console.log(a === b); // true

console.log(c === d); // false

在来看一段因为不当的代码导致的问题,大家一定要注意这部分的内容。

import React from ‘react’;

class Test extends React.PureComponent {

// 根据从 App 中传来的 animal 渲染组件

// 在 App 中每次点击添加新的动物后, 这里还是原来的 dog

render() {

return 

Test: {this.props.animal.join(‘,’)}
;

}

}

export default class App extends React.Component {

constructor(props) {

super(props);

// 默认为一只狗

this.state = { animal: [‘dog’] };

this.handleClick = this.handleClick.bind(this);

}

// 每次点击把新的值添加进 animal 中

// 此处有一个 Bug, 由于 animal.push 方法虽然更新了原来的数组

// 但是他们还是一个数组(这个说法有些奇怪), 指针还是一样的

// 可能需要读者自行搜索理解 JS 中基本类型和引用类型的存储方式

// 所以当 Test 组件接收到新的 animal 时, 通过浅比较会发现它们其实是一样的

// 也就意味着 Test 不会重新渲染

handleClick(val) {

const { animal } = this.state;

animal.push(val)

this.setState({

animal,

});

}

// 根据 state 中的 animal 渲染组件

render() {

return (

App: {this.state.animal.join(',')}

);

}

}

看到这里相信你应该能解答第二个问题和第三个问题了,不过我们还是一起再来看看~

问:如果我把Test组件的React.Component替换为React.PureComponent,结果与上面一样吗?如果不一样,为什么?

答:因为每次传递props中的onClick都是App组件中的handleTestClick,同时使用了PureComponent,所以每次浅比较都是一致的,所以不会在打印Test componentDidUpdate了。

问:如果我修改这一行代码<Test onClick={this.handleTestClick} /><Test onClick={() => {}} />结果又如何?

答:虽然使用了PureComponent,但是由于App每次调用render函数的时候都会重新声明一个方法,此方法和上一次传递给Test的方法不同,所以每次点击还是会打印Test componentDidUpdate

剩点内容补充


除了上述两个 API 以外,其他 API 或多或少只是它们的改版,所以我就放在一起说了。

memo

React.memo在我看来就是PureComponent无状态组件版本,如果用的是class就用PureComponent,如果用的是无状态组件就用memo

import React from ‘react’;

export default React.memo(function Test() {

return 

Test Component
;

});

// 它也可以接收第二个参数, 类似 shouldComponentUpdate

// 两个参数上次的props, 和当前的props

// 不传默认情况它们两个做浅比较, 传了由你自己控制

注意:此方法返回值与shouldComponentUpdate相反,返回值为true时不重新渲染组件。

useCallback 和 useMemo

这两个 API 是React Hook的一部分,为什么要放在一起说呢?因为它们非常的类似,据官方文档useCallback(fn, deps)相当于useMemo(() => fn, deps)

因为React Hook,我现在基本很少写class组件了,原因是什么相信用过的小伙伴都清楚,本文不阐述这方面的内容,只想再问你一个问题:handleClick方法每次都会重新定义吗?

import React from ‘react’;

export default function Test() {

const handleClick = () => console.log(‘click’);

return 

Test

}

答案是会的,不信你可以验证一下。

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-Gznu9JHp-1715824241488)]

[外链图片转存中…(img-00BXywbv-1715824241489)]

[外链图片转存中…(img-VemCaBv7-1715824241489)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值