第二章 设计高质量的React组件——《深入浅出React和Redux》读书笔记

2026-05-02 21:15:57 244
分类:react

第2章 设计高质量的React组件

        作为一个合格的开发者,不要只满足于编写出了可以运行的代码,而要了解代码背后的工作原理;不要只满足于自己编写的程序能够运行,还要让自己的代码可读而且易于维护。这样才能开发出高质量的软件。

        在这一章里,我们将深入介绍构建高质量React组件的原则和方法,包括以下内容:   

  • 划分组件边界的原则;   

  • React组件的数据种类;   

  • React组件的生命周期。

        本章的内容只是React组件设计的基础知识,因为React应用都是围绕组件的设计,所以关于组件的设计介绍将贯穿全书。

2.1 易于维护组件的设计要素

        当开发者发现一个组件功能太多代码量太大的时候,就要考虑拆分这个组件,用多个小的组件来代替。每个小的组件只关注实现单个功能,但是这些功能组合起来,也能满足复杂的实际需求。

        这就是“分而治之”的策略,把问题分解为多个小问题,这样既容易解决也方便维护,虽然“分而治之”是一个好策略,但是不要滥用,只有必要的时候才去拆分组件,不然可能得不偿失。

        拆分组件最关键的就是确定组件的边界,每个组件都应该是可以独立存在的,如果两个组件逻辑太紧密,无法清晰定义各自的责任,那也许这两个组件本身就不该被拆开,作为同一个组件也许更合理。

        虽然组件是应该独立存在的,但是并不是说组件就是孤岛一样的存在,不同组件之间总是会有通信交流,这样才可能组合起来完成更大的功能。

        作为软件设计的通则,组件的划分要满足高内聚(High Co-hesion)和低耦合(Low Coupling)的原则。

        高内聚指的是把逻辑紧密相关的内容放在一个组件中。react的JSX把html,css,js都放在同一个JavaScript文件中,所以说React天生具有高内聚的特点。

        低耦合指的是不同组件之间的依赖关系要尽量弱化,也就是每个组件要尽量独立。保持整个系统的低耦合度,需要对系统中的功能有充分的认识,然后根据功能点划分模块,让不同的组件去实现不同的功能,这个功夫还在开发者身上,不过,React组件的对外接口非常规范,方便开发者设计低耦合的系统。

2.2 React组件的数据

“差劲的程序员操心代码,优秀的程序员操心数据结构和它们之间的关系。”—Linus Torvalds,Linux创始人

        如何组织数据是程序的最重要问题。

        React组件的数据分为两种,prop和state,无论prop或者state的改变,都可能引发组件的重新渲染,那么,设计一个组件的时候,什么时候选择用prop什么时候选择用state呢?其实原则很简单,prop是组件的对外接口,state是组件的内部状态,对外用prop,内部用state。

2.2.1 React的prop

1. 给prop赋值

注:当prop的类型不是字符串类型时,在JSX中必须用花括号{}把prop值包住,所以style的值有两层花括号,外层花括号代表是JSX的语法,内层的花括号代表这是一个对象常量。

        当外部世界要传递一些数据给React组件,一个最直接的方式就是通过prop;同样,React组件要反馈数据给外部世界,也可以用prop。

2. 读取prop值

注:一定要记得在构造函数的第一行通过super调用父类也就是React.Component的构造函数。如果在构造函数中没有调用super(props),那么组件实例被构造之后,类实例的所有成员函数就无法通过this.props访问到父组件传递过来的props值。

3. propTypes检查

2.2.2 React的state

1.初始化state

2.读取和更新state

2.2.3 prop和state的对比

        总结一下prop和state的区别:  

  • prop用于定义外部接口,state用于记录内部状态;   

  • prop的赋值在外部世界使用组件时,state的赋值在组件内部;  

  • 组件不应该改变prop的值,而state存在的目的就是让组件来改变的。

        组件的state,就相当于组件的记忆,其存在意义就是被修改,每一次通过this.setState函数修改state就改变了组件的状态,然后通过渲染过程把这种变化体现出来。

2.3 组件的生命周期

react生命周期图.png

可以结合这篇文章来看

玩转 React(五)- 组件的内部状态和生命周期

2.3.1 装载过程

当组件第一次被渲染的时候,依次调用的函数是如下这些: 

  • constructor

  • getInitialState

  • getDefaultProps

  • componentWillMount

  • render

  • componentDidMount

1. constructor

一个React组件需要构造函数,往往是为了下面的目的:

  • 初始化state

  • 绑定成员函数的this环境

2. getInitialState和getDefaultProps

3. render

        通常一个组件要发挥作用,总是要渲染一些东西,render函数并不做实际的渲染动作,它只是返回一个JSX描述的结构,最终由React来操作渲染过程。

        render函数应该是一个纯函数,完全根据this.state和this.props来决定返回的结果,而且不要产生任何副作用。在render函数中去调用this.setState毫无疑问是错误的,因为一个纯函数不应该引起状态的改变。

4. componentWillMount和componentDidMount

        componentDidMount被调用的时候,render函数返回的东西已经引发了渲染,组件已经被“装载”到了DOM树上。

2.3.2 更新过程

        当props或者state被修改的时候,就会引发组件的更新过程。

        更新过程会依次调用下面的生命周期函数,其中render函数和装载过程一样,没有差别。

  • componentWillReceiveProps

  • shouldComponentUpdate

  • componentWillUpdate

  • render

  • componentDidUpdate

2.3.3 卸载过程

2.4 组件向外传递数据

        解决这个问题的方法,依然是利用prop。组件的prop可以是任何JavaScript对象,而在JavaScript中,函数是一等公民,函数本身就可以被看做一种对象,既可以像其他对象一样作为prop的值从父组件传递给子组件,又可以被子组件作为函数调用,这样事情就好办了。

2.5 React组件state和prop的局限

        组件之间通过状态提升来传递数据。使用React的state来存储状态的一个缺点,那就是数据的冗余和重复。

        数据如果出现重复,带来的一个问题就是如何保证重复的数据一致,如果数据存多份而且不一致,那就很难决定到底使用哪个数据作为正确结果了。

图2-5 组件状态不一致的困惑

        除了state,利用prop在组件之间传递信息也会遇到问题。设想一下,在一个应用中,包含三级或者三级以上的组件结构,顶层的祖父级组件想要传递一个数据给最低层的子组件,用prop的方式,就只能通过父组件中转,如图2-6所示。

        也许中间那一层父组件根本用不上这个prop,但是依然需要支持这个prop,扮演好搬运工的角色,只因为子组件用得上,这明显违反了低耦合的设计要求。第3章中我们会探讨如何解决这样的困局。

图2-6 跨级传递prop的困局

        另一种思路,就是干脆不要让任何一个React组件扮演“领头羊”的角色,把数据源放在React组件之外形成全局状态,如图2-7所示,让各个组件保持和全局状态的一致,这样更容易控制。

图2-7 React中提取出来

图2-6中所示,全局状态就是唯一可靠的数据源,这就是Flux和Redux中Store的概念。

2.6 本章小结

        在这一章中,我们学习了构建高质量组件的原则,应用Re-act一样要以构建高内聚低耦合的组件为目标,而保证组件高质量的一个重要工作就是保持组件对外接口清晰简洁。

        React利用prop来定义组件的对外接口,用state来代表内部的状态,某个数据选择用prop还是用state表示,取决于这个数据是对外还是对内。

        我们还介绍了React的生命周期,了解了装载过程、更新过程和卸载过程涉及的所有生命周期函数。