React 入门

在本文中,我们将向 React 问好。我们将详细了解它的背景和用例,在本地计算机上设置基本的 React 工具链,并创建一个简单的入门应用程序并进行试用——在此过程中学习 React 的工作原理。

预备知识 熟悉核心 HTMLCSSJavaScript 语言,以及 终端/命令行
学习成果 设置本地 React 开发环境,创建启动应用程序,并了解其工作原理的基础知识。

你好 React

正如其官方标语所述,React 是一个用于构建用户界面的库。React 不是一个框架——它甚至不局限于 Web。它与其他库一起用于渲染到特定环境。例如,React Native 可用于构建移动应用程序。

为了进行 Web 开发,开发人员将 React 与 ReactDOM 结合使用。React 和 ReactDOM 经常在相同的领域被讨论——并用于解决与其他真正的 Web 开发框架相同的问题。当我们把 React 称为“框架”时,我们是按照这种口语化的理解来使用的。

React 的主要目标是最大限度地减少开发人员构建 UI 时发生的错误。它通过使用组件来做到这一点——组件是自包含的、逻辑性的代码片段,描述了用户界面的一个部分。这些组件可以组合在一起创建完整的 UI,React 抽象了大部分渲染工作,让你专注于 UI 设计。

用例

与本模块中介绍的其他框架不同,React 不强制执行严格的代码约定或文件组织规则。这允许团队设置最适合他们的约定,并以他们希望的任何方式采用 React。React 可以处理一个按钮、界面的一些片段或应用程序的整个用户界面。

虽然 React 可以用于 界面的一小部分,但它不像 jQuery 等库,甚至 Vue 等框架那样容易“放入”应用程序——当你用 React 构建整个应用程序时,它更容易上手。

此外,React 应用程序的许多开发者体验优势,例如使用 JSX 编写界面,都需要编译过程。向网站添加 Babel 等编译器会使代码运行缓慢,因此开发人员通常会通过构建步骤来设置此类工具。React 可以说有很高的工具要求,但它是可以学习的。

本文将重点介绍使用 React 结合 Vite(一个现代前端构建工具)来渲染应用程序整个用户界面的用例。

React 如何使用 JavaScript?

React 利用现代 JavaScript 的特性来实现其许多模式。它与 JavaScript 最大的不同在于使用了 JSX 语法。JSX 扩展了 JavaScript 的语法,使得类似 HTML 的代码可以与其共存。例如:

jsx
const heading = <h1>Mozilla Developer Network</h1>;

这个标题常量被称为 JSX 表达式。React 可以使用它在我们的应用程序中渲染 <h1> 标签。

假设出于语义原因,我们想将标题包装在 <header> 标签中?JSX 方法允许我们像处理 HTML 一样将元素相互嵌套:

jsx
const header = (
  <header>
    <h1>Mozilla Developer Network</h1>
  </header>
);

注意: 上述代码片段中的括号并非 JSX 独有,对你的应用程序没有任何影响。它们只是向你(和你的计算机)表明括号内的多行代码属于同一个表达式。你也可以这样编写 header 表达式:

jsx
const header = <header>
  <h1>Mozilla Developer Network</h1>
</header>;

然而,这看起来有点笨拙,因为开始表达式的 <header> 标签没有与其相应的结束标签对齐。

当然,你的浏览器无法在没有帮助的情况下读取 JSX。当编译(使用 BabelParcel 等工具)后,我们的 header 表达式将如下所示:

jsx
const header = React.createElement(
  "header",
  null,
  React.createElement("h1", null, "Mozilla Developer Network"),
);

可以跳过编译步骤,使用 React.createElement() 来编写你自己的 UI。但是,这样做你会失去 JSX 的声明式优势,并且你的代码将变得更难阅读。编译是开发过程中的一个额外步骤,但 React 社区的许多开发人员认为 JSX 的可读性是值得的。此外,现代前端开发几乎总是涉及构建过程——你必须将现代语法降级以兼容旧浏览器,并且你可能希望 压缩 你的代码以优化加载性能。像 Babel 这样的流行工具已经自带 JSX 支持,因此除非你愿意,否则你无需自己配置编译。

由于 JSX 是 HTML 和 JavaScript 的混合体,一些开发者觉得它很直观。另一些则说它的混合性质使其令人困惑。然而,一旦你习惯了它,它将让你更快、更直观地构建用户界面,并让其他人一眼就能更好地理解你的代码库。

要了解更多关于 JSX 的信息,请查阅 React 团队的 使用 JSX 编写标记 文章。

设置你的第一个 React 应用程序

有许多方法可以创建一个新的 React 应用程序。我们将使用 Vite 通过命令行创建一个新的应用程序。

可以通过将一些 <script> 元素复制到 HTML 文件中来 将 React 添加到现有项目中,但使用 Vite 将让你有更多时间构建应用程序,而花更少的时间在设置上。

注意: 你可以通过 Scrimba 的 第一个 React 代码 MDN 学习伙伴 scrim 来开始编写 React 代码,而无需进行任何本地设置。在继续之前,请随意尝试一下。

依赖项

为了使用 Vite,你需要安装 Node.js。从 Vite 5.0 开始,至少需要 Node 18 或更高版本,并且在可能的情况下运行最新的长期支持 (LTS) 版本是个好主意。截至 2023 年 10 月 24 日,Node 20 是最新的 LTS 版本。Node 包括 npm(Node 包管理器)。

要检查你的 Node 版本,请在终端中运行以下命令:

bash
node -v

如果 Node 已安装,你将看到版本号。如果没有,你将看到错误消息。要安装 Node,请按照 Node.js 网站 上的说明进行操作。

你可以使用 Yarn 包管理器作为 npm 的替代品,但在这组教程中,我们将假设你使用 npm。有关 npm 和 yarn 的更多信息,请参阅 包管理基础

如果你正在使用 Windows,你需要安装一些软件来与 Unix/macOS 终端保持一致,以便使用本教程中提到的终端命令。Git Bash(作为 Windows 版 git 工具集 的一部分)或 适用于 Linux 的 Windows 子系统 (WSL) 都适用。有关这些工具以及一般终端命令的更多信息,请参阅 命令行速成课程

另外请记住,在使用这些教程时,React 和 ReactDOM 生成的应用程序仅适用于 Firefox、Microsoft Edge、Safari 或 Chrome 等相当现代的浏览器。

请参阅以下内容获取更多信息:

初始化你的应用程序

npm 包管理器附带了一个 `create` 命令,允许你从模板创建新项目。我们可以使用它从 Vite 的标准 React 模板创建一个新应用。确保你 `cd` 到你希望应用在你的机器上存在的目录,然后在你的终端中运行以下命令:

bash
npm create vite@latest moz-todo-react -- --template react

这会使用 Vite 的 `react` 模板创建一个 `moz-todo-react` 目录。

注意: -- 对于向 `create` 等 npm 命令传递参数是必需的,并且 --template react 参数告诉 Vite 使用其 React 模板。

如果此命令成功,你的终端会打印一些消息。你应该看到提示你 `cd` 到新目录、安装应用程序依赖项并在本地运行应用程序的文本。让我们从其中两个命令开始。在终端中运行以下命令:

bash
cd moz-todo-react && npm install

过程完成后,我们需要启动一个本地开发服务器来运行我们的应用程序。在这里,我们将向 Vite 的默认建议添加一些命令行标志,以便服务器启动后立即在浏览器中打开应用程序,并使用端口 3000。

在终端中运行以下命令:

bash
npm run dev -- --open --port 3000

服务器启动后,你应该会看到一个新的浏览器选项卡,其中包含你的 React 应用程序。

Screenshot of Firefox macOS open to localhost:3000, showing an application made from Vite's React template

应用程序结构

Vite 为我们提供了开发 React 应用程序所需的一切。其初始文件结构如下:

moz-todo-react
├── README.md
├── index.html
├── node_modules
├── package-lock.json
├── package.json
├── public
│   └── vite.svg
├── src
│   ├── App.css
│   ├── App.jsx
│   ├── assets
│   │   └── react.svg
│   ├── index.css
│   └── main.jsx
└── vite.config.js

index.html 是最重要的顶层文件。Vite 将你的代码注入到这个文件中,以便你的浏览器可以运行它。在本教程中你不需要编辑这个文件,但你应该更改此文件中 <title> 元素内的文本,以反映你的应用程序的标题。准确的页面标题对于可访问性很重要。

public 目录包含将直接提供给浏览器而无需经过 Vite 构建工具处理的静态文件。目前,它只包含一个 Vite 标志。

src 目录是我们大部分时间将花的地方,因为它是我们应用程序源代码所在的地方。你会注意到此目录中的某些 JavaScript 文件以 .jsx 扩展名结尾。对于包含 JSX 的任何文件,此扩展名是必需的——它告诉 Vite 将 JSX 语法转换为浏览器可以理解的 JavaScript。src/assets 目录包含你在浏览器中看到的 React 徽标。

`package.json` 和 `package-lock.json` 文件包含我们项目的元数据。这些文件并非 React 应用程序独有:Vite 为我们填充了 `package.json`,而 npm 在我们安装应用程序依赖项时创建了 `package-lock.json`。你无需完全理解这些文件即可完成本教程。但是,如果你想了解更多信息,可以在 npm 文档中阅读有关 `package.json``package-lock.json` 的内容。我们还在 包管理基础知识 教程中讨论了 `package.json`。

自定义我们的开发脚本

在我们继续之前,你可能需要稍微修改你的 package.json 文件,这样你就不必每次运行 npm run dev 时都传递 --open--port 标志了。在你的文本编辑器中打开 package.json 并找到 scripts 对象。将 "dev" 键更改为以下内容:

diff
- "dev": "vite",
+ "dev": "vite --open --port 3000",

这样设置后,每次运行 npm run dev,你的应用程序都会在浏览器中通过 https://:3000 打开。

注意: 这里你不需要额外的 --,因为我们是直接将参数传递给 vite,而不是传递给预定义的 npm 脚本。

探索我们的第一个 React 组件 — <App />

在 React 中,组件是一个可重用的模块,它渲染我们整个应用程序的一部分。组件可以大也可以小,但它们通常定义清晰:它们服务于一个单一、明确的目的。

让我们打开 src/App.jsx,因为我们的浏览器正在提示我们编辑它。此文件包含我们的第一个组件,<App />

jsx
import { useState } from "react";
import viteLogo from "/vite.svg";
import reactLogo from "./assets/react.svg";
import "./App.css";

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <a href="https://vite.ac.cn" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://reactjs.ac.cn" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.jsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  );
}

export default App;

App.jsx 文件由三个主要部分组成:顶部的 import 语句,中间的 App() 函数,以及底部的 export 语句。大多数 React 组件都遵循这种模式。

导入语句

文件顶部的 import 语句允许 App.jsx 使用在其他地方定义的代码。让我们更仔细地看看这些语句。

jsx
import { useState } from "react";
import viteLogo from "/vite.svg";
import reactLogo from "./assets/react.svg";
import "./App.css";

第一条语句从 react 库中导入了 useState hook。Hook 是一种在组件内部使用 React 特性的方式。我们将在本教程后面详细讨论 hook。

之后,我们导入 reactLogoviteLogo。请注意,它们的导入路径分别以 .// 开头,并以 .svg 扩展名结尾。这告诉我们这些导入是本地的,引用的是我们自己的文件而不是 npm 包。

最后一条语句导入了与我们的 <App /> 组件相关的 CSS。请注意,这里没有变量名,也没有 from 指令。这被称为 副作用导入——它不会将任何值导入到 JavaScript 文件中,但它会告诉 Vite 将引用的 CSS 文件添加到最终代码输出中,以便在浏览器中使用。

App() 函数

在导入之后,我们有一个名为 App() 的函数,它定义了 App 组件的结构。虽然大多数 JavaScript 社区喜欢像 helloWorld 这样的 小驼峰命名,但 React 组件使用 Pascal Case(或大驼峰命名)变量名,例如 HelloWorld,以明确给定的 JSX 元素是 React 组件而不是常规 HTML 标签。如果你将 App() 函数重命名为 app(),你的浏览器将抛出错误。

让我们仔细看看 App()

jsx
function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <a href="https://vite.ac.cn" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://reactjs.ac.cn" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.jsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  );
}

App() 函数返回一个 JSX 表达式。这个表达式定义了你的浏览器最终渲染到 DOM 的内容。

紧接着 return 关键字下方,有一个特殊的语法:<>。这是一个 片段。React 组件必须返回一个单独的 JSX 元素,而片段允许我们这样做,而无需在浏览器中渲染任意的 <div>。你会在许多 React 应用程序中看到片段。

export 语句

App() 函数之后还有一行代码:

jsx
export default App;

这个 export 语句使得我们的 App() 函数可以被其他模块使用。我们稍后会详细讨论这一点。

继续 main

让我们打开 src/main.jsx,因为 <App /> 组件正在那里使用。这个文件是我们应用程序的入口点,它最初看起来像这样:

jsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.jsx";

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

与 `App.jsx` 一样,该文件首先导入它运行所需的所有 JavaScript 模块和其他资产。

前两条语句从 reactreact-dom 库中导入 StrictModecreateRoot,因为它们稍后在文件中被引用。导入这些库时我们不写入路径或扩展名,因为它们不是本地文件。事实上,它们在我们的 package.json 文件中被列为依赖项。在学习本课时,请注意这种区别!

然后我们导入我们的 App() 函数和 index.css,其中包含应用于整个应用程序的全局样式。

然后我们调用 createRoot() 函数,它定义了我们应用程序的根节点。它将我们希望 React 应用程序渲染的 DOM 元素作为参数。在这种情况下,它是 ID 为 root 的 DOM 元素。最后,我们将 render() 方法链式调用到 createRoot() 调用上,并将我们希望在根节点内渲染的 JSX 表达式传递给它。通过将 <App /> 作为此 JSX 表达式编写,我们告诉 React 调用 App() 函数,该函数将 App 组件渲染到根节点内。

注意: <App /> 渲染在一个特殊的 <React.StrictMode> 组件内。此组件有助于开发人员捕获其代码中的潜在问题。

如果你愿意,可以查阅这些 React API:

重新开始

在开始构建我们的应用程序之前,我们将删除 Vite 为我们提供的一些样板代码。

首先,作为实验,将 App.jsx 中的 <h1> 元素更改为“Hello, World!”,然后保存文件。你会注意到此更改会立即在浏览器中运行的开发服务器 https://:3000 中渲染。在开发应用程序时请记住这一点。

我们不会使用其余代码!将 App.jsx 的内容替换为以下内容:

jsx
import "./App.css";

function App() {
  return (
    <>
      <header>
        <h1>Hello, World!</h1>
      </header>
    </>
  );
}

export default App;

JSX 实践

接下来,我们将使用我们的 JavaScript 技能,更轻松地编写 JSX 并使用 React 中的数据。我们将讨论如何向 JSX 元素添加属性、如何编写注释、如何从变量和其他表达式渲染内容,以及如何使用 props 将数据传递给组件。

向 JSX 元素添加属性

JSX 元素可以拥有属性,就像 HTML 元素一样。尝试在 App.jsx 文件中 <h1> 元素下方添加一个 <button>,如下所示:

jsx
<button type="button">Click me!</button>

当你保存文件时,你会看到一个带有“Click me!”字样的按钮。该按钮目前没有任何作用,但我们很快就会学习如何为我们的应用程序添加交互性。

某些属性与它们的 HTML 对应物不同。例如,HTML 中的 `class` 属性在 JSX 中转换为 `className`。这是因为 `class` 是 JavaScript 中的保留字,而 JSX 是 JavaScript 的扩展。如果你想为按钮添加一个 `primary` 类,你会这样写:

jsx
<button type="button" className="primary">
  Click me!
</button>

作为内容的 JavaScript 表达式

与 HTML 不同,JSX 允许我们直接与内容一起编写变量和其他 JavaScript 表达式。让我们在 App.jsx 文件中 App() 函数的上方声明一个名为 subject 的变量:

jsx
const subject = "React";
function App() {
  // code omitted for brevity
}

接下来,将 <h1> 元素中的单词“World”替换为 {subject}

jsx
<h1>Hello, {subject}!</h1>

保存文件并检查你的浏览器。你应该看到“Hello, React!”被渲染。

subject 周围的花括号是 JSX 语法的另一个特性。花括号告诉 React 我们要读取 subject 变量的值,而不是渲染字面字符串 "subject"。你可以在 JSX 的花括号内放置任何有效的 JavaScript 表达式;React 会对其进行求值并渲染表达式的结果作为最终内容。下面是一系列示例,上方有注释解释每个表达式将渲染什么:

jsx
{/* Hello, React :)! */}
<h1>Hello, {`${subject} :)`}!</h1>
{/* Hello, REACT */}
<h1>Hello, {subject.toUpperCase()}</h1>
{/* Hello, 4! */}
<h1>Hello, {2 + 2}!</h1>

即使是 JSX 中的注释也写在花括号里面!这是因为花括号可以包含一个 JavaScript 表达式,而注释作为 JavaScript 表达式的一部分是有效的(并且会被忽略)。你可以在花括号内使用 /* 块注释语法 */// 行注释语法(带尾随换行符)。

组件属性(props)

Props 是将数据传递到 React 组件的一种方式。它们的语法与属性(attribute)的语法相同,事实上:prop="value"。不同之处在于,属性是传递给普通元素的,而 props 是传递给 React 组件的。

在 React 中,数据流是单向的:props 只能从父组件向下传递到子组件。

让我们打开 main.jsx 并为我们的 <App /> 组件添加它的第一个 prop。

<App /> 组件调用添加一个 subject 属性,其值为 Clarice。完成后,它应该看起来像这样:

jsx
<App subject="Clarice" />

回到 App.jsx,让我们重新审视 App() 函数。更改 App() 的签名,使其接受 props 作为参数,并将 props 记录到控制台以便你可以检查它。还要删除 subject 常量;我们不再需要它了。你的 App.jsx 文件应该如下所示:

jsx
function App(props) {
  console.log(props);
  return (
    <>
      {
        // code omitted for brevity
      }
    </>
  );
}

保存文件并检查你的浏览器。你会看到一个空白背景,没有任何内容。这是因为我们正在尝试读取一个不再定义的 subject 变量。通过注释掉 <h1>Hello {subject}!</h1> 行来修复此问题。

注意: 如果你的代码编辑器能够解析 JSX(大多数现代编辑器都可以!),你可以使用其内置的注释快捷键——Ctrl + /(Windows 上)或 Cmd + /(macOS 上)——来更快地创建注释。

保存文件并注释掉那一行。这次,你应该会看到“Click me!”按钮独自渲染。如果你打开浏览器的开发人员控制台,你将看到如下消息:

Object { subject: "Clarice" }

对象属性 subject 对应于我们添加到 <App /> 组件调用中的 subject prop,字符串 Clarice 对应于其值。React 中的组件 props 总是以这种方式收集到对象中。

让我们使用这个 subject prop 来修复我们应用程序中的错误。取消注释 <h1>Hello, {subject}!</h1> 行并将其更改为 <h1>Hello, {props.subject}!</h1>,然后删除 console.log() 语句。你的代码应该如下所示:

jsx
function App(props) {
  return (
    <>
      <header>
        <h1>Hello, {props.subject}!</h1>
        <button type="button" className="primary">
          Click me!
        </button>
      </header>
    </>
  );
}

保存后,应用程序现在应该会显示“Hello, Clarice!”。如果你返回到 main.jsx,编辑 subject 的值,然后保存,你的文本将会改变。

为了进一步练习,你可以尝试在 main.jsx 中的 <App /> 组件调用中添加一个额外的 greeting prop,并在 App.jsx 中与 subject prop 一起使用它。

总结

至此,我们对 React 的初步了解就结束了,包括如何在本地安装它、创建入门应用程序以及基本原理。在下一篇文章中,我们将开始构建我们的第一个真正的应用程序——一个待办事项列表。不过在此之前,让我们回顾一下我们学到的一些知识。

在 React 中

  • 组件可以导入所需的模块,并且必须在文件底部导出自身。
  • 组件函数使用 PascalCase 命名。
  • 你可以通过将 JavaScript 表达式放在花括号之间,例如 {so},来在 JSX 中渲染它们。
  • 某些 JSX 属性与 HTML 属性不同,以免与 JavaScript 保留字冲突。例如,HTML 中的 class 在 JSX 中转换为 className
  • Props 的写法与组件调用中的属性完全相同,并传递给组件。

另见

学习 React MDN 学习伙伴

Scrimba 的 学习 React 课程是终极 React 101——是任何 React 初学者的完美起点。通过解决 140 多个交互式编码挑战和构建八个有趣的项目,学习现代 React 的基础知识。