React 入门

本文将带你初识 React。我们将了解其背景和用例,在本地计算机上设置基本的 React 工具链,并创建一个简单的入门应用并进行操作——在此过程中,学习 React 的工作原理。

先决条件

熟悉核心 HTMLCSSJavaScript 语言,了解 终端/命令行

React 使用一种名为 JSX(JavaScript 和 XML)的 HTML-in-JavaScript 语法。熟悉 HTML 和 JavaScript 将有助于你学习 JSX,并更好地识别应用程序中的错误是与 JavaScript 相关,还是与 React 的更具体领域相关。

目标

设置本地 React 开发环境,创建入门应用,并了解其工作原理的基础知识。

Hello 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 通过命令行创建一个新的应用程序。

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

要求

为了使用 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 终端保持一致,以便使用本教程中提到的终端命令。Gitbash(作为 git for Windows 工具集 的一部分)或Windows 子系统用于 LinuxWSL)都适用。有关这些内容以及终端命令的一般信息,请参阅 命令行速成课程

还要记住,在完成这些教程时,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 目录。

注意:-- 是必要的,用于将参数传递给 npm 命令(如 create),而 --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.jsonpackage-lock.json 文件包含有关我们项目的一些元数据。这些文件不是 React 应用程序独有的:Vite 为我们填充了 package.json,而 npm 在我们安装应用程序依赖项时创建了 package-lock.json。要完成本教程,你根本不需要理解这些文件。但是,如果你想了解更多信息,可以阅读 npm 文档中关于 package.jsonpackage-lock.json 的内容。我们还在 包管理基础知识 教程中讨论了 package.json

自定义我们的 dev 脚本

在我们继续之前,你可能希望稍微更改一下 package.json 文件,以便不必每次运行 npm run dev 时都传递 --open--port 标志。在文本编辑器中打开 package.json 并找到 scripts 对象。更改 "dev" 键,使其如下所示

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

有了它,每次运行 npm run dev 时,你的应用程序都将在 https://127.0.0.1:3000 上的浏览器中打开。

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

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

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

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

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

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

  return (
    <>
      <div>
        <a href="https://vite.vuejs.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 reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

第一个语句从 react 库导入 useState 钩子。钩子是在组件内部使用 React 功能的一种方式。我们将在本教程的后面详细讨论钩子。

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

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

App() 函数

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

让我们更仔细地看看 App()

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

  return (
    <>
      <div>
        <a href="https://vite.vuejs.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;

此导出语句使我们的 App() 函数可供其他模块使用。我们稍后将详细讨论。

继续到 main

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

jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.css";

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

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

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

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

然后我们调用 ReactDOM.createRoot() 函数,该函数定义了应用程序的根节点。它将 DOM 元素作为参数,我们希望在其中渲染 React 应用程序。在本例中,它是 ID 为 root 的 DOM 元素。最后,我们将 render() 方法链接到 createRoot() 调用,并将我们想要在根节点内渲染的 JSX 表达式传递给它。通过将 <App /> 作为此 JSX 表达式,我们告诉 React 调用 App() 函数,该函数在根节点内渲染 App 组件

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

如果您愿意,可以阅读有关这些 React API 的信息。

重新开始

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

首先,作为一个实验,更改 App.jsx 中的 `<h1>` 元素,使其显示“Hello, World!”,然后保存文件。您会注意到此更改会立即在浏览器中运行的 https://127.0.0.1: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() 函数上方声明一个名为 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 表达式。/* 块注释语法 */ 是为了让程序知道注释的开始和结束位置。

组件 props

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

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

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

<App /> 组件调用添加一个名为 subject 的 prop,其值为 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 属性与 HTML 属性不同,以避免与 JavaScript 保留字冲突。例如,HTML 中的 class 在 JSX 中转换为 className
  • Props 就像组件调用中的属性一样编写,并传递到组件中。

另请参阅

学习 React MDN 课程合作伙伴

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