首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >React16中的Component与PureComponent

React16中的Component与PureComponent

作者头像
挥刀北上
发布2020-02-20 13:49:35
1.2K0
发布2020-02-20 13:49:35
举报
文章被收录于专栏:Node.js开发Node.js开发

题图 From Bing By Clm

React中用类的方式声明组件的时候,一般需要继承Component这个类,但是在React16版本中增加了一个PureComponent类,这两个类有什么区别呢?

官方文档解释如下:

React.PureComponent 与 React.Component 很相似。两者的区别在于 React.Component 并未实现 shouldComponentUpdate(),而 React.PureComponent 中以浅层对比 prop 和 state 的方式来实现了该函数。 如果赋予 React 组件相同的 props 和 state,render() 函数会渲染相同的内容,那么在某些情况下使用 React.PureComponent 可提高性能。 https://zh-hans.reactjs.org/docs/react-api.html#reactpurecomponent

从文档中的描述中我们梳理出如下几点:

1. PureComponent与Component相似,都是用class定义组件时需要继承的类。

2. Component并未实现shouldComponentUpdate;

3. PuerComponent以浅层对比prop和state的方式实现了shouldComponentUpdate;

4. 如果赋予 React 组件相同的 props 和 state,render() 函数会渲染相同的内容,那么在某些情况下使用 React.PureComponent 可提高性能。

下面我们来逐条解释,第一点很好理解,在React中用class的方式定义组件,这两个类都可以使用。

第二点就有点疑惑了,但从字面意思理解“Component并未实现shouldComponentUpdate”是个什么意思呢?

我们先看一下shouldComponentUpdate函数的作用:我们知道,react组件中的state或者props发生变化后,组件是会重新渲染的,在这个过程中会触发组件的生命周期函数,首先会触发shouldComponentUpdate函数,这个函数会返回一个布尔值,默认值为true,返回true时会继续执行生命周期函数componentWillUpdate和componentDidUpdate。

如果返回fasle,组件的渲染会终止执行。流程如图:

此处因为篇幅所限,并未画出react的整个生命周期,只画出了组件在运行阶段的生命周期,这足以说明shouldComponentUpdate的作用了。

然后我们用一个案例来演示一下shouldComponentUpdate的用法。

在react中,父组件的state或者props发生变化组件会重新渲染,此时子组件也会重新渲染,但是有的时候子组件中的state或者props并未发生变化,也会被强制渲染,这里是不合理的,我们看一段代码:

import React, {Component, PureComponent} from 'react';
//父组件
class Parent extends Component{
    constructor() {
        super();
        //内部状态
        this.state = {count:0}
    }
    //父组件内部状态发生变化
    changeCount=()=>{
        this.setState({count:this.state.count+1})
    }


    render() {
        return <div>
            {this.state.count} <br/>
            子组件:<Child/><br/>
            <button onClick={this.changeCount}>改变count</button>
        </div>
    }
}

//子组件 用Component来生成
class Child extends Component{
    constructor() {
        super();
    }
    render(){
        //验证是否重新渲染
        console.log('我重新渲染了,关我毛事')
        return <div>
            我是子组件
        </div>
    }
}

export default Parent;

阅读源码,我们定义了两个组件,一个parent组件,一个child组件,parent组件中的state通过点击事件发生变化,触发setState,父组件会重新渲染,这也导致子组件重新渲染,多次点击按钮,浏览器打印结果如下:

我们发现,父组件重新渲染的同时,子组件也重新渲染了,但是子组件中的props和state并未发生变化,这是不必要的,此时shouldComponentUpdate的作用彰显出来了。

我们修改上面的代码,在子组件中添加shouldComponentUpdate,使其结果返回false。代码如下:

import React, {Component, PureComponent} from 'react';
//父组件
class Parent extends Component{
    constructor() {
        super();
        //内部状态
        this.state = {count:0}
    }
    //父组件内部状态发生变化
    changeCount=()=>{
        this.setState({count:this.state.count+1})
    }


    render() {
        return <div>
            {this.state.count} <br/>
            子组件:<Child/><br/>
            <button onClick={this.changeCount}>改变count</button>
        </div>
    }
}

//子组件 用Component来生成
class Child extends Component{
    constructor() {
        super();
    }
    //添加shouldComponentUpdate,使其执行结果返回false阻止组件重新渲染
    shouldComponentUpdate(nextProps, nextState, nextContext) {
        return false
    }

    render(){
        //验证是否重新渲染
        console.log('我重新渲染了,关我毛事')
        return <div>
            我是子组件
        </div>
    }
}

export default Parent;

此时点击按钮多次,发现父组件重新渲染但是子组件不会重新渲染了,这大大提高了组件的渲染效率。

看完上面的案例,相信大家对shouldComponentUpdate的作用应当有所了解了。

我们回过头看文档梳理的第二点:Component并未实现shouldComponentUpdate ,这里好像有点扯啊,用Component生成的组件明明可以使用shouldComponent这个函数啊,为什么说并未实现呢?

这里主要是文档翻译的有点不明所以,这里需要结合第三点来看,PuerComponent以浅层对比prop和state的方式实现了shouldComponentUpdate。

我们将上文的第一段代码修改一下,将子组件生成时用到的Component替换为PureComponent,代码如下:

import React, {Component, PureComponent} from 'react';
//父组件
class Parent extends Component{
    constructor() {
        super();
        //内部状态
        this.state = {count:0}
    }
    //父组件内部状态发生变化
    changeCount=()=>{
        this.setState({count:this.state.count+1})
    }
    render() {
        return <div>
            {this.state.count} <br/>
            子组件:<Child/><br/>
            <button onClick={this.changeCount}>改变count</button>
        </div>
    }
}
//子组件 用PureComponent来生成
class Child extends PureComponent{
    constructor() {
        super();
    }
    render(){
        //验证是否重新渲染
        console.log('我重新渲染了,关我毛事')
        return <div>
            我是子组件
        </div>
    }
}

export default Parent;

此时我们点击按钮,观察页面和打印结果,截图如下:

我们发现,当我们更改父组件中的state时,父组件发生渲染,但是子组件并未重新渲染。

由此我们得出,PuerComponent组件会自动使用shouldComponentUpdate对组件进行优化,只有组件的props或者state发生变化才会重新渲染,上面的案例,子组件没有props和state,也就没有发生变化,所以不需要重新渲染。

但是这里需要注意的是,PuerComponent中的shouldComponentUpdate对比组件渲染前后的props和state是浅对比,如果props或者state中的属性有对象或者数组,就会出现问题,因为对象或数组如果发生变化的只是值,而引用不变,那么PuerComponent中的shouldComponentUpdate就会判断不出来,导致props或state发生变化,而组件不会重新渲染。案例代码如下:

import React, {Component, PureComponent} from 'react';
//父组件
class App extends PureComponent{
    constructor() {
        super();
        //内部状态
        this.state = {
            arr:[],
            person:{
                name:'zs',
                age:19
            }}
    }
    //父组件内部状态发生变化
    changeCount=()=>{
        this.setState((prop)=>{
            prop.person.name="zs"+Math.random();
            return prop;
        })
    }
    addCount=()=>{
        this.setState((prop)=>{
            prop.arr.push(123);
            return prop
        })
    }
    render() {
        return <div>
            <p>arr:    {this.state.arr.length}</p>
            <button onClick={this.addCount}>改变arr</button>
            <br/>
            preson:{this.state.person.name} <br/>
            <button onClick={this.changeCount}>改变count</button>
        </div>
    }
}
export default App;

我们用PureComponent定义了一个App组件,在组件内部我们定义了person和arr数据,分别为对象和数组。

当我们分别点击按钮后,组件并不会渲染,这是因为PureComponent对props和state的改变只是进行的浅对比,类似浅拷贝,而person和arr是state的属性,这个两个属性的值发生变化,但引用没变,所以组件不会更新。此时就不能用PureComponent来代替Component了。

如果想让组件随着state和props的变化渲染可以将PureComponent改变为Component或者在person和arr改变后,对其引用重新设置,也会使组件重新渲染。来看代码:

import React, {Component, PureComponent} from 'react';
//父组件
class App extends PureComponent{
    constructor() {
        super();
        //内部状态
        this.state = {
            arr:[],
            person:{
                name:'zs',
                age:19
            }}
    }
    //父组件内部状态发生变化
    changeCount=()=>{
       // 利用扩展运算符拷贝person
       let person = this.state.person
        person.name = 'zs'+Math.random();
       this.setState({
           person:{...person}
       })
    }
    addCount=()=>{
        //利用扩展运算符拷贝arr
        let arr = this.state.arr;
        arr.push(1111);
        this.setState({
            arr:[...arr]
        })
    }
    render() {
        return <div>
            <p>arr:    {this.state.arr.length}</p>
            <button onClick={this.addCount}>改变arr</button>
            <br/>
            preson:{this.state.person.name} <br/>
            <button onClick={this.changeCount}>改变count</button>
        </div>
    }
}
export default App;

在代码中我们在改变person和arr后,通过扩展运算符重新拷贝了person和arr,此时运行代码,组件又可以渲染了,但是我们不能每次改变数据后进行一次拷贝,这里可以推荐大家使用immutable。关于immutable我们有机会再给大家介绍。

以上便是Componet组件与PureComponent组件的区别了。通俗的讲,PureComponent组件本质是对Componet组件的shulodComponentUpdate进行了优化:当组件的props或者state发生变化时,对其进行类似浅拷贝的浅对比,如果对比结果显示发生变化则返回true,组件会重新渲染,反之返回fasle,组件不会重新渲染。

我在测试的时候解决了PureComponent的一个小疑惑,看代码:

import React, {Component, PureComponent} from 'react';
//父组件
class App extends PureComponent{
    constructor() {
        super();
        //内部状态
        this.state = {
            count:0,
            arr:[],
            person:{
                name:'zs',
                age:19
            }}
    }
    //父组件内部状态发生变化
    changeCount=()=>{
        // 利用扩展运算符拷贝person
        let person = this.state.person
        person.name = 'zs'+Math.random();
        this.setState({
            count:Math.random(),
            person
        })
    }
    addCount=()=>{
        //利用扩展运算符拷贝arr
        let arr = this.state.arr;
        arr.push(1111);
        this.setState({
            arr,
            count:Math.random(),
        })
    }
    render() {
        return <div>
            <p>{this.state.count}</p>
            <p>arr:    {this.state.arr.length}</p>
            <button onClick={this.addCount}>改变arr</button>
            <br/>
            preson:{this.state.person.name} <br/>
            <button onClick={this.changeCount}>改变count</button>
        </div>
    }
}
export default App;

仔细阅读代码,我们发现不论我们更改person或者arr的时候,都没有进行拷贝,那么我们点击按钮时,组件中的person和arr会发生变化吗,代码中我们在修改person和arr的同时也修改了count,而count的值不是引用类型,shouldComponentUpdate判断就会返回ture组件重新渲染,那么同时组件中也会拿到person和arr的最新值变相的进行了渲染。

在React16之后,React官方建议大家使用Hooks的当时来写组件,也就是用函数来写组件,我们知道不管是PureComponent还是shouldComponentUpdate都是在类组件中的应用,如果用函数组件我们能不能也实现类似PureComponent的功能呢?那就是React的memo函数的功能了,我会另找时间和大家了聊一聊memo。

文章写完了,总结一下:

1、首先我们通过一个按钮给大家介绍了shouldComponentUpdate函数的作用,手动控制组件是否重新渲染。

2、通过案例介绍了如何利用shouldComponentUpdate对组件进行优化。

3、PureComponent组件出现的意义。

4、PureComponent组件的原理就是在shouldComponentUpdate函数中对state和props进行浅对比

最后给大家出一个思考题,既然原理都告诉你了,那么你能否实现一个类似PureComponent的类呢?

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-01-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 nodejs全栈开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档