React中的重要术语和概念

107 阅读11分钟

React应用程序的开发与传统Web应用程序的开发非常不同。例如,React采用了一种组件化的方法来构建应用程序。这意味着一个Web应用程序是由众多松散连接的小组件构成的。React通过一系列的优化,使得应用程序界面的变化可以以非常高效的方式显示。React的目标是能够在前端处理非常大量的数据。

组件和元素

如前所述,React的核心元素是组件。组件是应用程序的构建块,提供了应用程序界面的结构。一个组件的功能范围应该尽量小,应该专注于完成一个特定的任务或职责。同时,一个组件应该不依赖于外部环境的特定上下文或状态。这样可以使得组件在应用程序的多个位置重复使用,甚至在多个应用程序中使用。组件的核心是一个返回组件结构的函数,这个函数被称为渲染函数(render function)。

你可以用两种不同的方式来定义显示的界面元素结构:

  • 你可以使用React.createElement方法创建新的界面元素,并构建界面元素的层次结构。
  • 也可以使用JavaScript XML(JSX)——一种JavaScript的语法扩展——直接在组件的JavaScript源代码中使用类似HTML的语法来描述界面元素结构。

在本书中,常用的是第二种变体,即使用JSX。

组件并不是React中最小的单位。组件的最底层是React元素。React元素是对UI的抽象表示,会被React转换为浏览器中实际显示的HTML元素。因此,在浏览器环境中,React的div元素会被转换为HTML的div元素。在组件中,你可以使用组件和元素来描述应用程序的结构。为了区分元素和组件,React规定元素的名称总是以小写字母开头,而组件的名称以大写字母开头。

组件可以传递参数,从而提高组件的可重用性。如果你使用JSX,可以向组件传递属性,这些属性可以在JSX标签中进行传递。在React中,组件属性被简称为props,允许在组件树中传递对象。

通常,React组件有一个定义好的生命周期,你可以在不同的生命周期阶段调整组件,从而影响组件的行为。组件还可以有自己的状态,状态是一种数据结构,用于存储组件所需的信息。如果状态的值发生变化,React会将这些变化反映到浏览器中,并重新渲染组件。

React将组件分为两种类别:类组件和函数组件。

类组件

React的类组件在某种程度上已经成为过去的遗迹。在React16.8版本引入钩子API之前,类组件是组件的标准,因为只有类组件有状态和生命周期方法。然而,钩子API的引入使得类组件变得不那么重要了。在现代React应用程序中,几乎找不到类组件的踪迹。

类组件不在被提倡的原因在于函数组件更加符合React的特性。函数组件更轻量且更灵活。此外,钩子API解决了一些类组件的局限性。不过,我们会在第六章中更详细地讨论类组件的相关内容。

“类组件”这个名称源于其本质上是从React.Component基类派生出来的JavaScript类。在之前的React版本中,类组件是通过React.createClass方法创建的,然而但这个方法在现代React版本中已经被废弃因此。如果你要使用类组件,那么应该使用基于JavaScript类的语法(即ES6的class语法)来创建类组件。或者,你可以使用create-react-class包,这个包提供了一个与React.createClass方法非常相似的接口。

类组件的的核心部分是render方法。这个负责描述组件的UI结构,并返回一个React元素或组件。这些返回的元素将由React渲染并显示在浏览器的页面上。React通过类组件中的state属性来管理组件的状态。类组件通过一系列生命周期方法(如componentDidMount、componentDidUpdate和componentWillUnmount等)来管理组件的生命周期。

当你开始开发一个新的React应用时,应该避免使用类组件,而是采用更现代的函数组件。

函数组件

顾名思义,函数组件是由一个函数组成的组件。函数组件本质上与类组件的render方法相同,同样是负责渲染组件。在React初期版本中,函数组件默认没有状态和生命周期方法。函数组件的原始用途是用于简单的显示组件。然而,随着钩子API的引入,这种情况发生了变化,函数组件变成了具有状态和生命周期的完整的React组件,现在完全取代了类组件。除少数情况外,本书在介绍开发应用程序时乎使用函数组件。

数据流

React应用程序的生命力在于数据的动态性。用户在使用React应用程序时,既能通过交互创造新的信息,又能从应用程序中获取有用的信息。构建React应用程序的一个核心要素是建模数据流。在应用程序的组件树中,信息总是从父元素向子元素传递。

如果你想要开发一款用于管理信息的React应用程序,通常第一个需求是实现信息列表的展示。在组件树中,列表由一个组件表示,列表中的每个条目也是组件,列表条目与列表组件之间存在父子关系。

列表条目是列表的子元素(见图1.2)。要显示信息,为了显示信息,您将数据读取到一个中心点,比如列表组件,并将信息分发给各个子元素。这种解决方案具有决定性的优势,即只需要一个汇总请求即可获取数据,从而能够立即获得概览。

图1.2 React应用程序中的数据流

List组件包含状态,也就是说,List组件负责数据管理。List组件从服务器下载要显示的数据,并负责将更改的数据发送到服务器。对于每条数据记录,都会创建一个子组件,这些子组件通过props接收要显示的信息。这种方法确保了可以实现有向的数据流,使得React能够高效地渲染界面。组件的这种划分带来的一个副作用是可以在应用程序的多个地方重用组件。

但是在这种数据流方式中,子组件无法将信息传递回父组件。但当子组件中的信息被修改且需要通知父组件时,这种数据反向流动就变得非常必要。解决方案之一是除了传递渲染信息外,还从父组件向子组件传递回调函数。这些函数称为事件处理程序,子组件在特定情况下(例如,当数据发生变化时)会执行这些函数。这些函数在父组件的作用域内运行,因此可以访问父组件的内部结构,例如修改状态。这样,子组件就可以将信息返回给父组件。

有向数据流带来的性能优势来自于React能够精确地确定UI的哪些部分需要更新。从而优化了应用程序的渲染过程。

渲染器

在React应用程序的默认配置会默认安装react和react-dom包。react包包含了React库,而react-dom包则包含了渲染器。渲染器的作用是将React元素转换为具体的HTML元素,这些HTML元素可以在浏览器中进行渲染。

另一个React渲染器是 React Native,用于将React应用的元素转换为iOS或Android应用中的原生等效元素。如果你使用不同的渲染器,需要注意,不同的环境使用的是不同的元素。例如,你可以在react-dom中使用div元素,而在 React Native中则使用view元素作为容器元素。

除了在浏览器或移动设备上渲染React应用,还有许多其他渲染器可供选择,例如NPM包中的Ink渲染器,这个渲染用于使用React构建交互式命令行应用程序。

渲染器算法

React包中包含了渲染器算法(Reconciler)。这个算法负责检测应用程序中的变化。对组件的调整可以通过更改props或state来实现。为了使更改在浏览器中生效,浏览器环境下的DOM树必须至少部分重建。渲染HTML结构仍然是Web应用程序性能的一个主要弱点。React提供了一个解决方案,即虚拟DOM,它是应用程序结构的JavaScript对象表示。

如果需要修改DOM结构,React会生成一个新的虚拟DOM。这个虚拟DOM作为应用程序的新版本蓝图。随后,React会尝试通过一系列优化的操作来调整现有的实际DOM。

为了优化渲染器算法,做出了两个假设以减少复杂性:

ž 两个不同类型的元素会生成不同的DOM树。

ž key属性可以用来指定在两次渲染步骤之间哪些子元素保持不变。

在比较原始状态和目标状态时,React会从根元素开始,逐步向子元素进行比较。

正如之前提到的,渲染器算法首先检查两个元素的类型是否匹配。如果类型不同,算法的第一个优化措施就会生效。这个优化措施是将不同类型的元素会生成不同的树,见图1.3。

图1.3不同的DOM树结构

如果两个元素的类型不同相同,React会停止当前的比较过程,构建一个全新的子DOM树。这意味着之前构建的所有结构都会被销毁,所有相关的组件也会被卸载。组件的生命周期在此过程中会被终止,并执行必要的卸载逻辑以清理组件环境。接着,React会创建新的组件,并执行与新组件相关的生命周期函数。

如果两个元素的类型匹配,那么接下来会检查元素的属性,并仅对属性值进行相应的调整。例如,如果元素的类名或样式属性发生变化,则相应的DOM元素就会被调整,而子DOM树不会被销毁(参见图1.4)。

图1.4修改DOM树结构中的属性

对于组件,情况与属性发生变化的元素类似:组件实例保持不变,因此组件状态会被保留并执行相应的生命周期函数。然后再处理子元素和组件。

多个子元素和key属性

在有多个元素的列表中,可能只有列表项元素顺序发生改变,而元素本身没有改变的。尤其是在列表的某个位置插入新元素时,所有后续元素的索引都会发生变化,这会导致React认为后面的列表需要被重新构建。通过使用key属性可以避免这种情况。key属性在列表中必须包含一个唯一且稳定的值。如果父组件的render方法再次执行,将使用key属性的值来检查列表元素是否发生了变化。

通常使用数据记录的唯一ID作为key属性的值。这个值不一定是数字,但必须在列表中是唯一的,但必须在组件的不同渲染过程中保持不变。否则React无法准确地将DOM元素与组件状态进行匹配和更新。

在开发过程中,React会在每次渲染列表时发出警告,提示缺少key属性的元素。这条警告指出,列表中的每个子元素都应该有一个唯一的key属性。特别是对于大型列表,使用key属性可以显著提高渲染性能。

通过这些概念,你已经初步了解了React的世界。本书的后续章节将以这些术语为基础,提供更多详细的解释。在下一节中,您将了解一些经常与React结合使用的库。