Skip to content
This page has been auto-translated and may contain errors.View in English

历史和版本

React 已经存在足够长的时间,经历了几个不同的时代。了解这段历史的大致轮廓,能帮助你读懂野生代码。一份 2017 年的教程和一份今天的教程,虽然都是讲 React,但看起来可能完全不同。本章带你走一遍时间线,解释你会遇到的两种写法。

简要时间线

React 诞生于 Facebook,当时需要让大型、数据密集的界面保持同步,又不想陷入手动 DOM 更新的泥沼。它在 2013 年作为开源项目发布,核心理念是:根据当前数据描述 UI,让 React 处理更新。这个理念从未改变。围绕这个核心,React 的其他几乎一切都在演进。

最初的几年里,一个需要记住状态的组件是用 JavaScript class 写的。Class 是普通的 JavaScript 特性,React 用它让组件有地方存放随时间变化的数据,还有一套特殊命名的方法供 React 在特定时刻调用:组件首次出现时、更新时、被移除时。这种风格曾是默认选择,这也是为什么现存这么多 React 代码都是这样写的。

大转变来自 Hooks,它在 2019 年初的 React 16.8 中推出。Hook 是 React 给你的函数,让普通函数组件能做之前只有 class 才能做的事,比如在多次渲染之间记住一个值。写组件变得简洁得多,函数组件很快成了新代码的默认选择。Hooks 后面会详细介绍。现在要记住的是,Hooks 让现代写法成为可能。

React 之后还在不断演进:React 18 在 2022 年发布,React 19 在 2024 年发布,每个版本都增加了新能力、磨平了一些粗糙的地方。这些版本对构建真实应用最有意义,后续相关章节会讲到。

这就是整体的弧线。你不需要死记日期。重要的是,React 有段长历史,核心原理保持稳定,写法已经明确转向函数组件和 Hooks。

野生代码中的两种写法

正因为这段历史,你会遇到两种明显不同的 React 写法。

较早的写法把组件写成 class。数据存在 class 里,React 在组件生命周期的特定时刻调用特殊命名的方法。大量生产代码仍以这种方式运行,许多老教程、博客文章和 Stack Overflow 答案都是这样写的。它仍然有效,仍然被支持。它只是写 React 的早期方法。

旧风格长什么样

本课程不教这种风格,你也不需要写它。但值得认得出来,因为你会遇上。老代码库里的组件看起来大概这样:

jsx
class Counter extends React.Component {
  state = { count: 0 }

  componentDidMount() {
    // React 会调用这个,就在组件首次出现在屏幕后
  }

  render() {
    return <button>点击了 {this.state.count} 次</button>
  }
}

三个特征标记它:顶部的 classextends React.Component,用 this.state 存放变化的数据,以及 component 开头的方法名供 React 在特定时刻自动调用。

现代写法把组件写成 函数,用 Hooks 赋予它记忆力和响应变化的能力。新的 React 代码都是这样写的,官方文档现在也首先教的是这种方法。

本课程教的是现代 React:始终用函数组件和 Hooks。在你可能碰到旧式 class 写法的地方,不管是工作里的代码库还是老教程,我们会指出来,这样两种写法在你脑子里就能对应上,也不会被其中任何一个打懵。

Hooks 之后的两个版本值得了解一下。React 18 在 2022 年带来了并发渲染:React 获得了在后台准备更新、中断工作来保持界面响应的能力。它也改变了应用的启动方式,把入口改到 createRoot,这是应用选择使用这些新能力的方式。

React 19 在 2024 年继续这个方向。它添加了 Actions 用于处理表单提交和待机状态,添加了 use Hook 用于在渲染期间读取 promises 和 context 这样的资源,还让你可以把 ref 作为普通 prop 传递,而不用用 forwardRef。随着版本发布,React 编译器自动化了记忆化:开发者以前用 useMemouseCallback 手工做的工作,现在可以在构建时处理。

Hooks 取代了 class,而不是跟它并存,因为 class 模型让有状态逻辑很难共享。在组件间复用行为意味着用高阶组件包装它们,或者用 render props 穿线,两者都会让你的树嵌套加深,逻辑的真实位置也变得模糊。Hooks 让你把逻辑提取成普通函数(自定义 Hook),从任何组件调用它。复用的故事变得简单得多。Hooks 还绕过了 this:class 组件强迫你绑定方法,还要推理 this 在调用时指什么,这是一个反复出现的 bug 源头,而函数组件没有这个问题。

React 18 里的并发工作比 createRoot 的改变要深得多。它改变了渲染本身的行为方式,远低于你调用的 API 层。React 现在可以开始渲染一个更新,暂停它,然后继续或放弃它,这意味着渲染不再保证一口气从头到尾运行完。startTransition 和 Suspense 驱动的数据加载这样的特性建立在这个基础上。这是为什么 createRoot 的意义超越了仅仅是新函数名的原因:选择使用它是什么给了 React 权限以这种方式调度和中断渲染。

从这里开始,课程用现代风格构建,Components 是正式开始的地方:一个函数形式的组件返回 UI 的一部分。在任何东西运行之前,你需要在自己的机器上安装 React 并让它运行。

Juno两种写法,一个 React 如果你找到一份教程,组件是用 classthis.statecomponentDidMount 写的,别慌,也别觉得自己做错了。那是 React 的旧风格,它仍然有效。

本课程用的是新风格,函数组件配 Hooks,我会在我们经过旧式写法时指出来,这样不会有什么让你措手不及。

Juno两种写法,一个 React 读真实代码的快速地图:用 this.state 和生命周期方法的 class 是 pre-Hooks 风格,用 useStateuseEffect 的函数是现代 React。工作中两种都会见到。

我们这里用现代风格构建,碰到你在实际工作中会遇到的 class 式写法时我会指出来。

Juno两种写法,一个 React 要区分的版本:16.8 的 Hooks 改变了默认的编写风格,修复了没有 HOCs 或 render props 的逻辑复用;18 引入了并发渲染,把入口点改到 createRoot;19 添加了 Actions、use Hook 和 ref 作为 prop。

Hooks 和并发是两个深入渲染语义的转变,也是值得在机制层面理解的。

接下来:搭建 React 项目,你会在自己的机器上跑起 React。