From 67b4a24d64b39ab29718daccb801ca3e15f3898f Mon Sep 17 00:00:00 2001 From: Xavi Lee Date: Mon, 3 Apr 2023 07:51:59 +0800 Subject: [PATCH 01/23] docs(cn): translate learn/tutorial-tic-tac-toe into Chinese --- src/content/learn/tutorial-tic-tac-toe.md | 449 +++++++++++----------- src/sidebarLearn.json | 2 +- 2 files changed, 224 insertions(+), 227 deletions(-) diff --git a/src/content/learn/tutorial-tic-tac-toe.md b/src/content/learn/tutorial-tic-tac-toe.md index bf65f8398a..cf7a96fc82 100644 --- a/src/content/learn/tutorial-tic-tac-toe.md +++ b/src/content/learn/tutorial-tic-tac-toe.md @@ -1,31 +1,30 @@ --- -title: 'Tutorial: Tic-Tac-Toe' +title: '教程:井字棋游戏' --- -You will build a small tic-tac-toe game during this tutorial. This tutorial does not assume any existing React knowledge. The techniques you'll learn in the tutorial are fundamental to building any React app, and fully understanding it will give you a deep understanding of React. +本教程将实现一个小型井字棋游戏,并且你不需要对 React 有任何了解。在此过程中你会学习到一些 React 基本知识,这些知识可以让你对 React 有更深入的了解。 -This tutorial is designed for people who prefer to **learn by doing** and want to quickly try making something tangible. If you prefer learning each concept step by step, start with [Describing the UI.](/learn/describing-the-ui) +本教程专为喜欢 **理论与实战相结合** 以及希望快速看见成果的人而设计。如果你喜欢逐步学习每个概念,请从 [描述 UI](/learn/describing-the-ui) 开始。 -The tutorial is divided into several sections: +教程分成以下几个部分: -- [Setup for the tutorial](#setup-for-the-tutorial) will give you **a starting point** to follow the tutorial. -- [Overview](#overview) will teach you **the fundamentals** of React: components, props, and state. -- [Completing the game](#completing-the-game) will teach you **the most common techniques** in React development. -- [Adding time travel](#adding-time-travel) will give you **a deeper insight** into the unique strengths of React. +- [配置](#setup-for-the-tutorial) 是一些准备工作。 +- [概览](#overview) 介绍了 React 的 **基础知识**:组件、prop 和 state。 +- [完成游戏](#completing-the-game) 介绍了 React 开发中 **最常用的技术**。 +- [添加“时间旅行”](#adding-time-travel) 可以让你更深入地了解 React 的独特优势。 +### 将会构建什么? {/*what-are-you-building*/} -### What are you building? {/*what-are-you-building*/} +本教程将使用 React 构建一个可以交互的井字棋游戏。 -In this tutorial, you'll build an interactive tic-tac-toe game with React. - -You can see what it will look like when you're finished here: +你可以在下面预览最终成果: @@ -194,15 +193,15 @@ body { -If the code doesn't make sense to you yet, or if you are unfamiliar with the code's syntax, don't worry! The goal of this tutorial is to help you understand React and its syntax. +如果你还不是很明白上面的代码,不用担心!本教程的目的就是帮你理解 React 及其语法。 -We recommend that you check out the tic-tac-toe game above before continuing with the tutorial. One of the features that you'll notice is that there is a numbered list to the right of the game's board. This list gives you a history of all of the moves that have occurred in the game, and it is updated as the game progresses. +我们建议你在继续本教程之前,先看看上面的井字棋游戏。我们会注意到的一项功能是,棋盘右侧有一个编号列表,它记录了游戏中落子的历史,并随着游戏的进行而更新。 -Once you've played around with the finished tic-tac-toe game, keep scrolling. You'll start with a simpler template in this tutorial. Our next step is to set you up so that you can start building the game. +体验完游戏以后,继续阅读本教程吧!我们将从一个更简单的模板开始。下一步将介绍相关配置,以便于你可以开始构建这个游戏。 -## Setup for the tutorial {/*setup-for-the-tutorial*/} +## 配置 {/*setup-for-the-tutorial*/} -In the live code editor below, click **Fork** in the top-right corner to open the editor in a new tab using the website CodeSandbox. CodeSandbox lets you write code in your browser and preview how your users will see the app you've created. The new tab should display an empty square and the starter code for this tutorial. +在下面的实时代码编辑器中,单击右上角的 **Fork**,将访问网站 CodeSandbox,在新选项卡中打开编辑器。CodeSandbox 让你能够在浏览器中编写代码并预览效果。你应该能够看见一个空方块和本教程的初始代码。 @@ -261,33 +260,33 @@ body { -You can also follow this tutorial using your local development environment. To do this, you need to: +你也可以使用本地开发环境来跟着这个教程学习,你需要: -1. Install [Node.js](https://nodejs.org/en/) -1. In the CodeSandbox tab you opened earlier, press the top-left corner button to open the menu, and then choose **File > Export to ZIP** in that menu to download an archive of the files locally -1. Unzip the archive, then open a terminal and `cd` to the directory you unzipped -1. Install the dependencies with `npm install` -1. Run `npm start` to start a local server and follow the prompts to view the code running in a browser +1. 安装 [Node.js](https://nodejs.org/en/) +2. 在之前打开的 CodeSandbox 选项卡中,按左上角的按钮打开菜单,然后选择 **File > Export to ZIP**,将代码压缩包下载到本地。 +3. 将压缩包解压,打开终端并使用 `cd` 命令切换到你解压后的目录。 +4. 使用 `npm install` 安装依赖。 +5. 运行 `npm start` 启动本地服务器,按照提示在浏览器中查看运行效果。 -If you get stuck, don't let this stop you! Follow along online instead and try a local setup again later. +如果你卡住了,不要让它挡道。请改为在线进行操作,稍后再尝试本地配置。 -## Overview {/*overview*/} +## 概览 {/*overview*/} -Now that you're set up, let's get an overview of React! +完成配置以后,我们来大概看一看 React 是什么样子吧! -### Inspecting the starter code {/*inspecting-the-starter-code*/} +### 看一下刚刚的代码 {/*inspecting-the-starter-code*/} -In CodeSandbox you'll see three main sections: +在 CodeSandbox 中,你将看到三个主要的部分: ![CodeSandbox with starter code](../images/tutorial/react-starter-code-codesandbox.png) -1. The _Files_ section with a list of files like `App.js`, `index.js`, `styles.css` and a folder called `public` -1. The _code editor_ where you'll see the source code of your selected file -1. The _browser_ section where you'll see how the code you've written will be displayed +1. _Files_ 部分列出了一些文件:`App.js`、`index.js`、`styles.css` 和一个叫 `public` 的文件夹。 +2. _code editor_ 里面你能够看到打开的代码。 +3. _browser_ 部分你将看到代码的实时效果。 -The `App.js` file should be selected in the _Files_ section. The contents of that file in the _code editor_ should be: +`App.js` 文件里面的内容应该是这样的: ```jsx export default function Square() { @@ -295,15 +294,14 @@ export default function Square() { } ``` -The _browser_ section should be displaying a square with a X in it like this: - +_browser_ 部分应该会像下面这样在方块里面显示一个 X: ![x-filled square](../images/tutorial/x-filled-square.png) -Now let's have a look at the files in the starter code. +现在,让我们仔细来看一看这些文件吧。 #### `App.js` {/*appjs*/} -The code in `App.js` creates a _component_. In React, a component is a piece of reusable code that represents a part of a user interface. Components are used to render, manage, and update the UI elements in your application. Let's look at the component line by line to see what's going on: +`App.js` 的代码创建了一个 _组件_。在 React 中,组件是一段可重用代码,它通常作为 UI 界面的一部分。组件用于渲染、管理和更新应用中的 UI 元素。让我们逐行查看这段代码,看看发生了什么: ```js {1} export default function Square() { @@ -311,7 +309,7 @@ export default function Square() { } ``` -The first line defines a function called `Square`. The `export` JavaScript keyword makes this function accessible outside of this file. The `default` keyword tells other files using your code that it's the main function in your file. +第一行定义了一个名为 `Square` 的函数。`export` JavaScript 关键字使此函数可以在此文件之外访问。`default` 关键字表明它是文件中的主要函数。 ```js {2} export default function Square() { @@ -319,15 +317,14 @@ export default function Square() { } ``` -The second line returns a button. The `return` JavaScript keyword means whatever comes after is returned as a value to the caller of the function. `` closes the JSX element to indicate that any following content shouldn't be placed inside the button. +第二行返回一个按钮。`return` JavaScript 关键字意味着后面的内容都作为值返回给函数的调用者。`` 闭合 JSX 元素表示不应将任何后续内容放置在按钮内。 #### `styles.css` {/*stylescss*/} -Click on the file labeled `styles.css` in the _Files_ section of CodeSandbox. This file defines the styles for your React app. The first two _CSS selectors_ (`*` and `body`) define the style of large parts of your app while the `.square` selector defines the style of any component where the `className` property is set to `square`. In your code, that would match the button from your Square component in the `App.js` file. - +单击 CodeSandbox 中的 `styles.css` 文件。该文件定义了 React 应用的样式。前两个 CSS 选择器(`*` 和 `body`)定义应用大部分的样式,而 `.square` 选择器定义 `className` 属性设置为 `square` 的任何组件的样式。这将与 `App.js` 文件中的 `Square` 组件中的按钮相匹配。 #### `index.js` {/*indexjs*/} -Click on the file labeled `index.js` in the _Files_ section of CodeSandbox. You won't be editing this file during the tutorial but it is the bridge between the component you created in the `App.js` file and the web browser. +单击 CodeSandbox 中的 `index.js` 的文件。在本教程中我们不会编辑此文件,但它是 `App.js` 文件中创建的组件与 Web 浏览器之间的桥梁。 ```jsx import {StrictMode} from 'react'; @@ -337,20 +334,20 @@ import './styles.css'; import App from './App'; ``` -Lines 1-5 brings all the necessary pieces together: +将第 1-5 行将所有必要的部分组合在一起: * React -* React's library to talk to web browsers (React DOM) -* the styles for your components -* the component you created in `App.js`. +* React 与 Web 浏览器对话的库(React DOM) +* 组件的样式 +* `App.js` 里面创建的组件 -The remainder of the file brings all the pieces together and injects the final product into `index.html` in the `public` folder. +其他文件将它们组合在一起,并将最终成果注入 `public` 文件夹里面的 `index.html` 中。 -### Building the board {/*building-the-board*/} +### 构建棋盘 {/*building-the-board*/} -Let's get back to `App.js`. This is where you'll spend the rest of the tutorial. +让我们回到 `App.js`。接下来我们将聚焦于这个文件。 -Currently the board is only a single square, but you need nine! If you just try and copy paste your square to make two squares like this: +目前棋盘只有一个方块,但你需要九个!如果你只是想着复制粘贴来制作两个像这样的正方形: ```js {2} export default function Square() { @@ -358,7 +355,7 @@ export default function Square() { } ``` -You'll get this error: +你将会得到如下错误: @@ -366,7 +363,7 @@ You'll get this error: -React components need to return a single JSX element and not multiple adjacent JSX elements like two buttons. To fix this you can use *fragments* (`<>` and ``) to wrap multiple adjacent JSX elements like this: +React 组件只能返回单个 JSX 元素,而不是像两个按钮那样的多个相邻的 JSX 元素。要解决此问题,可以使用 `<>` 和 `` 来包裹多个相邻的 JSX 元素,如下所示: ```js {3-6} export default function Square() { @@ -379,17 +376,17 @@ export default function Square() { } ``` -Now you should see: +现在你应该可以看见: ![two x-filled squares](../images/tutorial/two-x-filled-squares.png) -Great! Now you just need to copy-paste a few times to add nine squares and... +非常棒!现在你只需要通过复制粘贴来添加九个方块,然后…… ![nine x-filled squares in a line](../images/tutorial/nine-x-filled-squares.png) -Oh no! The squares are all in a single line, not in a grid like you need for our board. To fix this you'll need to group your squares into rows with `div`s and add some CSS classes. While you're at it, you'll give each square a number to make sure you know where each square is displayed. +哦不!这些方块都在一条直线上,而不是排列成网格。要解决此问题,需要使用 `div` 将方块分到每一行中并添加一些 CSS 样式。当你这样做的时候,需要给每个方块一个数字,以确保你知道每个方块的位置。 -In the `App.js` file, update the `Square` component to look like this: +`App.js` 文件中,`Square` 组件看起来像这样: ```js {3-19} export default function Square() { @@ -415,11 +412,11 @@ export default function Square() { } ``` -The CSS defined in `styles.css` styles the divs with the `className` of `board-row`. Now that you've grouped your components into rows with the styled `div`s you have your tic-tac-toe board: + `styles.css` 成功设置了样式!现在我们已经使用样式化的 `div` 将组件分组到行中,你拥有了井字棋棋盘: ![tic-tac-toe board filled with numbers 1 through 9](../images/tutorial/number-filled-board.png) -But you now have a problem. Your component named `Square`, really isn't a square anymore. Let's fix that by changing the name to `Board`: +但是现在有个问题,名为 `Square` 的组件实际上不再是正方形了。让我们通过将名称更改为 Board 来解决这个问题: ```js {1} export default function Board() { @@ -427,7 +424,7 @@ export default function Board() { } ``` -At this point your code should look something like this: +此时你的代码应如下所示: @@ -504,15 +501,15 @@ body { -Psssst... That's a lot to type! It's okay to copy and paste code from this page. However, if you're up for a little challenge, we recommend only copying code that you've manually typed at least once yourself. +嘶,要输入的内容太多了!可以从该页面复制和粘贴代码。但是,如果你愿意迎接一点挑战,我们建议只复制你自己至少手动输入过一次的代码。 -### Passing data through props {/*passing-data-through-props*/} +### 通过 prop 传递数据 {/*passing-data-through-props*/} -Next, you'll want to change the value of a square from empty to "X" when the user clicks on the square. With how you've built the board so far you would need to copy-paste the code that updates the square nine times (once for each square you have)! Instead of copy-pasting, React's component architecture allows you to create a reusable component to avoid messy, duplicated code. +接下来,当用户单击正方形时,我们要将正方形的值从空更改为“X”。根据目前构建的棋盘,你需要复制并粘贴九次更新方块的代码(每个方块都需要一次)!但是,React 的组件架构可以创建可重用的组件,以避免混乱、重复的代码。 -First, you are going to copy the line defining your first square (``) from your `Board` component into a new `Square` component: +首先,要将定义第一个方块(``)的这行代码从 `Board` 组件复制到新的 `Square` 组件中: ```js {1-3} function Square() { @@ -524,7 +521,7 @@ export default function Board() { } ``` -Then you'll update the Board component to render that `Square` component using JSX syntax: +然后,将更新 Board 组件并使用 JSX 语法渲染 `Square` 组件: ```js {5-19} // ... @@ -551,15 +548,15 @@ export default function Board() { } ``` -Note how unlike the browser `div`s, your own components `Board` and `Square` must start with a capital letter. +需要注意的是,这并不像 `div`,这些你自己的组件如 `Board` 和 `Square`,必须以大写字母开头。 -Let's take a look: +让我们来看一看效果: ![one-filled board](../images/tutorial/board-filled-with-ones.png) -Oh no! You lost the numbered squares you had before. Now each square says "1". To fix this, you will use *props* to pass the value each square should have from the parent component (`Board`) to its child (`Square`). +哦不!你失去了你以前有正确编的方块。现在每个方块都写着“1”。要解决此问题,需要使用 prop 将每个方块应具有的值从父组件(`Board`)传递到其子组件(`Square`)。 -Update the `Square` component to read the `value` prop that you'll pass from the `Board`: +更新 `Square` 组件,读取将从 `Board` 传递的 `value` prop: ```js {1} function Square({ value }) { @@ -567,9 +564,9 @@ function Square({ value }) { } ``` -`function Square({ value })` indicates the Square component can be passed a prop called `value`. +`function Square({ value })` 表示可以向 Square 组件传递一个名为 `value` 的 prop。 -Now you want to display that `value` instead of `1` inside every square. Try doing it like this: +现在你如果想要显示对应的 `value` 而不是 `1`,可以试一下像下面这样: ```js {2} function Square({ value }) { @@ -577,11 +574,11 @@ function Square({ value }) { } ``` -Oops, this is not what you wanted: +糟糕!这还不是你想要的: ![value-filled board](../images/tutorial/board-filled-with-value.png) -You wanted to render the JavaScript variable called `value` from your component, not the word "value". To "escape into JavaScript" from JSX, you need curly braces. Add curly braces around `value` in JSX like so: +我们需要从组件中渲染名为 `value` 的 JavaScript 变量,而不是“value”这个词。要从 JSX“转义到 JavaScript”,你需要大括号。在 JSX 中的 `value` 周围添加大括号,如下所示: ```js {2} function Square({ value }) { @@ -589,11 +586,11 @@ function Square({ value }) { } ``` -For now, you should see an empty board: +现在,你应该会看到一个空的棋盘了: ![empty board](../images/tutorial/empty-board.png) -This is because the `Board` component hasn't passed the `value` prop to each `Square` component it renders yet. To fix it you'll add the `value` prop to each `Square` component rendered by the `Board` component: +这是因为 `Board` 组件还没有将 `value` prop 传递给它渲染的每个 `Square` 组件。要修复这个问题,需要向 `Board` 组件里面的的每个 `Square` 组件添加 `value` prop : ```js {5-7,10-12,15-17} export default function Board() { @@ -619,11 +616,11 @@ export default function Board() { } ``` -Now you should see a grid of numbers again: +现在你应该能再次看到数字网格: ![tic-tac-toe board filled with numbers 1 through 9](../images/tutorial/number-filled-board.png) -Your updated code should look like this: +更新后的代码应该是这样: @@ -702,9 +699,9 @@ body { -### Making an interactive component {/*making-an-interactive-component*/} +### 创建一个具有交互性的组件 {/*making-an-interactive-component*/} -Let's fill the `Square` component with an `X` when you click it. Declare a function called `handleClick` inside of the `Square`. Then, add `onClick` to the props of the button JSX element returned from the `Square`: +当你单击它的时候,应该用 `X` 来填充 `Square` 组件。在 `Square` 内部声明一个名为 `handleClick` 的函数。然后,将 `onClick` 添加到由 `Square` 返回的按钮的 JSX 元素的 prop 中: ```js {2-4,9} function Square({ value }) { @@ -723,19 +720,19 @@ function Square({ value }) { } ``` -If you click on a square now, you should see a log saying `"clicked!"` in the _Console_ tab at the bottom of the _Browser_ section in CodeSandbox. Clicking the square more than once will log `"clicked!"` again. Repeated console logs with the same message will not create more lines in the console. Instead, you will see an incrementing counter next to your first `"clicked!"` log. +如果现在单击一个方块,你应该会看到一条日志,上面写着 `"clicked!"` 在 CodeSandbox 中浏览器部分底部的控制台选项卡中。多次单击方块将再次记录 `"clicked!"`。具有相同消息的重复控制台日志不会在控制台中创建更多行。相反,你会在第一次 `"clicked!"` 旁边看到一个递增的计数器。 -If you are following this tutorial using your local development environment, you need to open your browser's Console. For example, if you use the Chrome browser, you can view the Console with the keyboard shortcut **Shift + Ctrl + J** (on Windows/Linux) or **Option + ⌘ + J** (on macOS). +如果使用本地开发环境学习本教程,则需要打开浏览器的控制台。例如,如果使用 Chrome 浏览器,则可以使用键盘快捷键 **Shift + Ctrl + J**(在 Windows/Linux 上)或 **Option + ⌘ + J**(在 macOS 上)查看控制台。 -As a next step, you want the Square component to "remember" that it got clicked, and fill it with an "X" mark. To "remember" things, components use *state*. +下一步,我们希望 Square 组件能够“记住”它被单击过,并用“X”填充它。为了“记住”一些东西,组件使用 *state*. -React provides a special function called `useState` that you can call from your component to let it "remember" things. Let's store the current value of the `Square` in state, and change it when the `Square` is clicked. +React 提供了一个名为 `useState` 的特殊函数,可以从组件中调用它来让它“记住”一些东西。让我们将 `Square` 的当前值存储在 `state` 中,并在单击 `Square` 时更改它。 -Import `useState` at the top of the file. Remove the `value` prop from the `Square` component. Instead, add a new line at the start of the `Square` that calls `useState`. Have it return a state variable called `value`: +在文件的顶部导入 `useState`。从 `Square` 组件中移除 value prop。相反地,在调用 `useState` 的 `Square` 的开头添加一个新行。让它返回一个名为 value 的 state 变量: ```js {1,3,4} import { useState } from 'react'; @@ -747,9 +744,9 @@ function Square() { //... ``` -`value` stores the value and `setValue` is a function that can be used to change the value. The `null` passed to `useState` is used as the initial value for this state variable, so `value` here starts off equal to `null`. +`value 存储值`,而 `setValue` 是可用于更改值的函数。传递给 `useState` 的 `null` 用作这个 state 变量的初始值,因此此处 `value` 的值开始时等于 null。 -Since the `Square` component no longer accepts props anymore, you'll remove the `value` prop from all nine of the Square components created by the Board component: +由于 `Square` 组件不再接受 prop,将从 Board 组件创建的所有九个 Square 组件中删除 `value` prop: ```js {6-8,11-13,16-18} // ... @@ -776,7 +773,7 @@ export default function Board() { } ``` -Now you'll change `Square` to display an "X" when clicked. Replace the `console.log("clicked!");` event handler with `setValue('X');`. Now your `Square` component looks like this: +现在将更改 `Square` 以在单击时显示“X”。替换掉 `console.log("clicked!");` 使用 `setValue('X');` 的事件处理程序。现在你的 `Square` 组件看起来像这样: ```js {5} function Square() { @@ -797,13 +794,13 @@ function Square() { } ``` -By calling this `set` function from an `onClick` handler, you're telling React to re-render that `Square` whenever its `` 闭合 JSX 元素表示不应将任何后续内容放置在按钮内。 +第二行返回一个按钮。`return` JavaScript 关键字意味着后面的内容都作为值返回给函数的调用者。`` 闭合 JSX 元素以表示不应将任何后续内容放置在按钮内。 #### `styles.css` {/*stylescss*/} -单击 CodeSandbox 中的 `styles.css` 文件。该文件定义了 React 应用的样式。前两个 CSS 选择器(`*` 和 `body`)定义应用大部分的样式,而 `.square` 选择器定义 `className` 属性设置为 `square` 的任何组件的样式。这将与 `App.js` 文件中的 `Square` 组件中的按钮相匹配。 +单击 CodeSandbox 中的 `styles.css` 文件。该文件定义了 React 应用的样式。前两个 CSS 选择器(`*` 和 `body`)定义了应用大部分的样式,而 `.square` 选择器定义了 `className` 属性设置为 `square` 的组件的样式。这与 `App.js` 文件中的 `Square` 组件中的按钮是相匹配的。 #### `index.js` {/*indexjs*/} @@ -380,11 +380,11 @@ export default function Square() { 现在你应该可以看见: -![two x-filled squares](../images/tutorial/two-x-filled-squares.png) +![两个“X”方块](../images/tutorial/two-x-filled-squares.png) 非常棒!现在你只需要通过复制粘贴来添加九个方块,然后…… -![nine x-filled squares in a line](../images/tutorial/nine-x-filled-squares.png) +![9 个在同一行的方块](../images/tutorial/nine-x-filled-squares.png) 哦不!这些方块都在一条直线上,而不是排列成网格。要解决此问题,需要使用 `div` 将方块分到每一行中并添加一些 CSS 样式。当你这样做的时候,需要给每个方块一个数字,以确保你知道每个方块的位置。 @@ -416,7 +416,7 @@ export default function Square() { `styles.css` 成功设置了样式!现在我们已经使用样式化的 `div` 将组件分组到行中,你拥有了井字棋棋盘: -![tic-tac-toe board filled with numbers 1 through 9](../images/tutorial/number-filled-board.png) +![有着数字 1 到 9 的井字棋棋盘](../images/tutorial/number-filled-board.png) 但是现在有个问题,名为 `Square` 的组件实际上不再是正方形了。让我们通过将名称更改为 Board 来解决这个问题: @@ -523,7 +523,7 @@ export default function Board() { } ``` -然后,将更新 Board 组件并使用 JSX 语法渲染 `Square` 组件: +然后,更新 Board 组件并使用 JSX 语法渲染 `Square` 组件: ```js {5-19} // ... @@ -554,9 +554,9 @@ export default function Board() { 让我们来看一看效果: -![one-filled board](../images/tutorial/board-filled-with-ones.png) +![都是 1 的方块](../images/tutorial/board-filled-with-ones.png) -哦不!你失去了你以前有正确编号的方块。现在每个方块都写着“1”。要解决此问题,需要使用 *prop* 将每个方块应具有的值从父组件(`Board`)传递到其子组件(`Square`)。 +哦不!你失去了你以前有正确编号的方块。现在每个方块都写着“1”。要解决此问题,需要使用 *prop* 将每个方块应有的值从父组件(`Board`)传递到其子组件(`Square`)。 更新 `Square` 组件,读取从 `Board` 传递的 `value` prop: @@ -578,9 +578,9 @@ function Square({ value }) { 糟糕!这还不是你想要的: -![value-filled board](../images/tutorial/board-filled-with-value.png) +![都是“value”的棋盘](../images/tutorial/board-filled-with-value.png) -我们需要从组件中渲染名为 `value` 的 JavaScript 变量,而不是“value”这个词。要从 JSX“转义到 JavaScript”,你需要大括号。在 JSX 中的 `value` 周围添加大括号,如下所示: +我们需要从组件中渲染名为 `value` 的 JavaScript 变量,而不是“value”这个词。要从 JSX“转义到 JavaScript”,你需要使用大括号。在 JSX 中的 `value` 周围添加大括号,如下所示: ```js {2} function Square({ value }) { @@ -590,9 +590,9 @@ function Square({ value }) { 现在,你应该会看到一个空的棋盘了: -![empty board](../images/tutorial/empty-board.png) +![空棋盘](../images/tutorial/empty-board.png) -这是因为 `Board` 组件还没有将 `value` prop 传递给它渲染的每个 `Square` 组件。要修复这个问题,需要向 `Board` 组件里面的的每个 `Square` 组件添加 `value` prop: +这是因为 `Board` 组件还没有将 `value` prop 传递给它渲染的每个 `Square` 组件。要修复这个问题,需要向 `Board` 组件里面的每个 `Square` 组件添加 `value` prop: ```js {5-7,10-12,15-17} export default function Board() { @@ -620,7 +620,7 @@ export default function Board() { 现在你应该能再次看到数字网格: -![tic-tac-toe board filled with numbers 1 through 9](../images/tutorial/number-filled-board.png) +![有着数字 1 到 9 的井字棋棋盘](../images/tutorial/number-filled-board.png) 更新后的代码应该是这样: @@ -703,7 +703,7 @@ body { ### 创建一个具有交互性的组件 {/*making-an-interactive-component*/} -当你单击它的时候,应该用 `X` 来填充 `Square` 组件。在 `Square` 内部声明一个名为 `handleClick` 的函数。然后,将 `onClick` 添加到由 `Square` 返回的按钮的 JSX 元素的 prop 中: +当你单击它的时候,`Square` 组件需要显示“X”。在 `Square` 内部声明一个名为 `handleClick` 的函数。然后,将 `onClick` 添加到由 `Square` 返回的 JSX 元素的 button 的 prop 中: ```js {2-4,9} function Square({ value }) { @@ -722,7 +722,7 @@ function Square({ value }) { } ``` -如果现在单击一个方块,你应该会看到一条日志,上面写着 `"clicked!"` 在 CodeSandbox 中 *Browser* 部分底部的 *Console* 选项卡中。多次单击方块将再次记录 `"clicked!"`。具有相同消息的重复控制台日志不会在控制台中创建更多行。相反,你会在第一次 `"clicked!"` 旁边看到一个递增的计数器。 +如果现在单击一个方块,你应该会看到一条日志,上面写着 `"clicked!"`。在 CodeSandbox 中 *Browser* 部分底部的 *Console* 选项卡中。多次单击方块将再次记录 `"clicked!"`。具有相同消息的重复控制台日志不会在控制台中重复创建。而你会在第一次 `"clicked!"` 旁边看到一个递增的计数器。 @@ -730,11 +730,11 @@ function Square({ value }) { -下一步,我们希望 Square 组件能够“记住”它被单击过,并用“X”填充它。为了“记住”一些东西,组件使用 *state*. +下一步,我们希望 Square 组件能够“记住”它被单击过,并用“X”填充它。为了“记住”一些东西,组件使用 *state*。 React 提供了一个名为 `useState` 的特殊函数,可以从组件中调用它来让它“记住”一些东西。让我们将 `Square` 的当前值存储在 `state` 中,并在单击 `Square` 时更改它。 -在文件的顶部导入 `useState`。从 `Square` 组件中移除 `value` prop。相反地,在调用 `useState` 的 `Square` 的开头添加一个新行。让它返回一个名为 value 的 state 变量: +在文件的顶部导入 `useState`。从 `Square` 组件中移除 `value` prop。在调用 `useState` 的 `Square` 的开头添加一个新行。让它返回一个名为 value 的 state 变量: ```js {1,3,4} import { useState } from 'react'; @@ -748,7 +748,7 @@ function Square() { `value` 存储值,而 `setValue` 是可用于更改值的函数。传递给 `useState` 的 `null` 用作这个 state 变量的初始值,因此此处 `value` 的值开始时等于 null。 -由于 `Square` 组件不再接受 prop,将从 Board 组件创建的所有九个 Square 组件中删除 `value` prop: +由于 `Square` 组件不再接受 prop,我们从 Board 组件创建的所有九个 Square 组件中删除 `value` prop: ```js {6-8,11-13,16-18} // ... @@ -775,7 +775,7 @@ export default function Board() { } ``` -现在将更改 `Square` 以在单击时显示“X”。替换掉 `console.log("clicked!");` 使用 `setValue('X');` 的事件处理程序。现在你的 `Square` 组件看起来像这样: +现在将更改 `Square` 以在单击时显示“X”。不再使用 `console.log("clicked!");` 而使用 `setValue('X');` 的事件处理程序。现在你的 `Square` 组件看起来像这样: ```js {5} function Square() { @@ -796,11 +796,11 @@ function Square() { } ``` -通过从 `onClick` 处理程序调用此 `set` 函数,你告诉 `React` 在单击其 ``)的这行代码从 `Board` 组件复制到新的 `Square` 组件中: @@ -1188,7 +1188,7 @@ export default function Board() { } ``` -注意新的 `() =>` 语法。这里,`() => handleClick(0)` 是一个箭头函数,它是定义函数的一种较短的方式。单击正方形时,`=>`“箭头”之后的代码将运行,调用 `handleClick(0)`。 +注意新的 `() =>` 语法。这里,`() => handleClick(0)` 是一个箭头函数,它是定义函数的一种较短的方式。单击方块时,`=>`“箭头”之后的代码将运行,调用 `handleClick(0)`。 现在你需要更新其他八个方块以从你传递的箭头函数中调用 `handleClick`。确保 `handleClick` 的每次调用的参数对应于正确的 square 索引: @@ -1826,7 +1826,7 @@ function Board({ xIsNext, squares, onPlay }) { } ``` -现在,将 `Board` 组件里面的 `handleClick` 中的 `setSquares` 和 `setXIsNext` 调用替换为对新 `onPlay` 函数的一次调用,这样 `Game` 组件就可以在用户单击正方形时更新 `Board`: +现在,将 `Board` 组件里面的 `handleClick` 中的 `setSquares` 和 `setXIsNext` 调用替换为对新 `onPlay` 函数的一次调用,这样 `Game` 组件就可以在用户单击方块时更新 `Board`: ```js {12} function Board({ xIsNext, squares, onPlay }) { @@ -1863,7 +1863,7 @@ export default function Game() { } ``` -在这里,`[...history, nextSquares]` 创建了一个新数组,其中包含 `history` 中的所有元素,后跟 `nextSquares`。(你可以将 `...history` 展开语法理解为“枚举 `history` 中的所有项目”。) +在这里,`[...history, nextSquares]` 创建了一个新数组,其中包含 `history` 中的所有元素,后跟 `nextSquares`。(你可以将 `...history` 展开语法理解为“枚举 `history` 中的所有元素”。) 例如,如果 `history` 是 `[[null,null,null], ["X",null,null]]`,`nextSquares` 是 `["X",null,"O"]`,那么新的 `[...history, nextSquares]` 数组就是 `[[null,null,null], ["X",null,null], ["X",null,"O"]]`。 @@ -2022,7 +2022,7 @@ body { 像 `` 闭合 JSX 元素以表示不应将任何后续内容放置在按钮内。 +第二行返回一个按钮。JavaScript 的 `return` 关键字意味着后面的内容都作为值返回给函数的调用者。`` 闭合 JSX 元素以表示不应将任何后续内容放置在按钮内。 #### `styles.css` {/*stylescss*/} @@ -337,7 +337,7 @@ import './styles.css'; import App from './App'; ``` -将第 1-5 行将所有必要的部分组合在一起: +第 1-5 行将所有必要的部分组合在一起: * React * React 与 Web 浏览器对话的库(React DOM) @@ -348,7 +348,7 @@ import App from './App'; ### 构建棋盘 {/*building-the-board*/} -让我们回到 `App.js`。接下来我们将聚焦于这个文件。 +让我们回到 `App.js`。接下来我们将专注于这个文件。 目前棋盘只有一个方块,但你需要九个!如果你只是想着复制粘贴来制作两个像这样的方块: @@ -366,7 +366,7 @@ export default function Square() { -React 组件只能返回单个 JSX 元素,而不是像两个按钮那样的多个相邻的 JSX 元素。要解决此问题,可以使用 `<>` 和 `` 来包裹多个相邻的 JSX 元素,如下所示: +React 组件必须返回单个 JSX 元素,不能像两个按钮那样返回多个相邻的 JSX 元素。要解决此问题,可以使用 `<>` 和 `` 来包裹多个相邻的 JSX 元素,如下所示: ```js {3-6} export default function Square() { @@ -387,7 +387,7 @@ export default function Square() { ![9 个在同一行的方块](../images/tutorial/nine-x-filled-squares.png) -哦不!这些方块都在一条直线上,而不是排列成网格。要解决此问题,需要使用 `div` 将方块分到每一行中并添加一些 CSS 样式。当你这样做的时候,需要给每个方块一个数字,以确保你知道每个方块的位置。 +但事与愿违的是这些方块并没有排列成网格,而是都在一条线上。要解决此问题,需要使用 `div` 将方块分到每一行中并添加一些 CSS 样式。当你这样做的时候,需要给每个方块一个数字,以确保你知道每个方块的位置。 `App.js` 文件中,`Square` 组件看起来像这样: @@ -415,7 +415,7 @@ export default function Square() { } ``` -`styles.css` 成功设置了样式!现在我们已经使用样式化的 `div` 将组件分组到行中,你拥有了井字棋棋盘: +借助 `styles.css` 中定义的 `board-row` 样式,我们将组件分到每一行的 `div` 中。最终完成了井字棋棋盘: ![有着数字 1 到 9 的井字棋棋盘](../images/tutorial/number-filled-board.png) @@ -504,7 +504,7 @@ body { -嘶,要输入的内容太多了!可以从该页面复制和粘贴代码。但是,如果你愿意迎接一点挑战,我们建议只复制你自己至少手动输入过一次的代码。 +嘶……要改的的内容也太多了!从该页面复制和粘贴代码是很好的办法。不过如果你愿意挑战一下自己,可以只复制手动输入过的代码。 From e9328596121a42ac2036400f4097370e95a730a1 Mon Sep 17 00:00:00 2001 From: Xavi Lee Date: Sun, 9 Apr 2023 21:43:38 +0800 Subject: [PATCH 15/23] restore props --- src/content/learn/tutorial-tic-tac-toe.md | 60 +++++++++++------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/content/learn/tutorial-tic-tac-toe.md b/src/content/learn/tutorial-tic-tac-toe.md index 8bd2da4c12..9742bcfb6f 100644 --- a/src/content/learn/tutorial-tic-tac-toe.md +++ b/src/content/learn/tutorial-tic-tac-toe.md @@ -17,7 +17,7 @@ title: '教程:井字棋游戏' 教程分成以下几个部分: - [配置](#setup-for-the-tutorial) 是一些准备工作。 -- [概览](#overview) 介绍了 React 的 **基础知识**:组件、prop 和 state。 +- [概览](#overview) 介绍了 React 的 **基础知识**:组件、props 和 state。 - [完成游戏](#completing-the-game) 介绍了 React 开发中 **最常用的技术**。 - [添加“时间旅行”](#adding-time-travel) 可以让你更深入地了解 React 的独特优势。 @@ -508,7 +508,7 @@ body { -### 通过 prop 传递数据 {/*passing-data-through-props*/} +### 通过 props 传递数据 {/*passing-data-through-propss*/} 接下来,当用户单击方块时,我们要将方块的值从空更改为“X”。根据目前构建的棋盘,你需要复制并粘贴九次更新方块的代码(每个方块都需要一次)!但是,React 的组件架构可以创建可重用的组件,以避免混乱、重复的代码。 @@ -557,9 +557,9 @@ export default function Board() { ![都是 1 的方块](../images/tutorial/board-filled-with-ones.png) -哦不!你失去了你以前有正确编号的方块。现在每个方块都写着“1”。要解决此问题,需要使用 *prop* 将每个方块应有的值从父组件(`Board`)传递到其子组件(`Square`)。 +哦不!你失去了你以前有正确编号的方块。现在每个方块都写着“1”。要解决此问题,需要使用 *props* 将每个方块应有的值从父组件(`Board`)传递到其子组件(`Square`)。 -更新 `Square` 组件,读取从 `Board` 传递的 `value` prop: +更新 `Square` 组件,读取从 `Board` 传递的 `value` props: ```js {1} function Square({ value }) { @@ -567,7 +567,7 @@ function Square({ value }) { } ``` -`function Square({ value })` 表示可以向 Square 组件传递一个名为 `value` 的 prop。 +`function Square({ value })` 表示可以向 Square 组件传递一个名为 `value` 的 props。 现在你如果想要显示对应的 `value` 而不是 `1`,可以试一下像下面这样: @@ -593,7 +593,7 @@ function Square({ value }) { ![空棋盘](../images/tutorial/empty-board.png) -这是因为 `Board` 组件还没有将 `value` prop 传递给它渲染的每个 `Square` 组件。要修复这个问题,需要向 `Board` 组件里面的每个 `Square` 组件添加 `value` prop: +这是因为 `Board` 组件还没有将 `value` props 传递给它渲染的每个 `Square` 组件。要修复这个问题,需要向 `Board` 组件里面的每个 `Square` 组件添加 `value` props: ```js {5-7,10-12,15-17} export default function Board() { @@ -704,7 +704,7 @@ body { ### 创建一个具有交互性的组件 {/*making-an-interactive-component*/} -当你单击它的时候,`Square` 组件需要显示“X”。在 `Square` 内部声明一个名为 `handleClick` 的函数。然后,将 `onClick` 添加到由 `Square` 返回的 JSX 元素的 button 的 prop 中: +当你单击它的时候,`Square` 组件需要显示“X”。在 `Square` 内部声明一个名为 `handleClick` 的函数。然后,将 `onClick` 添加到由 `Square` 返回的 JSX 元素的 button 的 props 中: ```js {2-4,9} function Square({ value }) { @@ -735,7 +735,7 @@ function Square({ value }) { React 提供了一个名为 `useState` 的特殊函数,可以从组件中调用它来让它“记住”一些东西。让我们将 `Square` 的当前值存储在 `state` 中,并在单击 `Square` 时更改它。 -在文件的顶部导入 `useState`。从 `Square` 组件中移除 `value` prop。在调用 `useState` 的 `Square` 的开头添加一个新行。让它返回一个名为 value 的 state 变量: +在文件的顶部导入 `useState`。从 `Square` 组件中移除 `value` props。在调用 `useState` 的 `Square` 的开头添加一个新行。让它返回一个名为 value 的 state 变量: ```js {1,3,4} import { useState } from 'react'; @@ -749,7 +749,7 @@ function Square() { `value` 存储值,而 `setValue` 是可用于更改值的函数。传递给 `useState` 的 `null` 用作这个 state 变量的初始值,因此此处 `value` 的值开始时等于 null。 -由于 `Square` 组件不再接受 prop,我们从 Board 组件创建的所有九个 Square 组件中删除 `value` prop: +由于 `Square` 组件不再接受 props,我们从 Board 组件创建的所有九个 Square 组件中删除 `value` props: ```js {6-8,11-13,16-18} // ... @@ -899,7 +899,7 @@ body { ### React 开发者工具 {/*react-developer-tools*/} -React 开发者工具可以检查 React 组件的 prop 和 state。可以在 CodeSandbox 的 *Browser* 部分底部找到 React DevTools 选项卡: +React 开发者工具可以检查 React 组件的 props 和 state。可以在 CodeSandbox 的 *Browser* 部分底部找到 React DevTools 选项卡: ![CodeSandbox 中的 React 开发者工具](../images/tutorial/codesandbox-devtools.png) @@ -921,9 +921,9 @@ React 开发者工具可以检查 React 组件的 prop 和 state。可以在 Cod 目前,每个 `Square` 组件都维护着游戏 state 的一部分。要检查井字棋游戏中的赢家,`Board` 需要以某种方式知道 9 个 `Square` 组件中每个组件的 state。 -你会如何处理?起初,你可能会猜测 `Board` 需要向每个 `Square`“询问”`Square` 的 state。尽管这种方法在 React 中在技术上是可行的,但我们不鼓励这样做,因为代码变得难以理解、容易出现错误并且难以重构。相反,最好的方法是将游戏的 state 存储在 `Board` 父组件中,而不是每个 `Square` 中。`Board` 组件可以通过传递一个 prop 来告诉每个 `Square` 显示什么,就像你将数字传递给每个 Square 时所做的那样。 +你会如何处理?起初,你可能会猜测 `Board` 需要向每个 `Square`“询问”`Square` 的 state。尽管这种方法在 React 中在技术上是可行的,但我们不鼓励这样做,因为代码变得难以理解、容易出现错误并且难以重构。相反,最好的方法是将游戏的 state 存储在 `Board` 父组件中,而不是每个 `Square` 中。`Board` 组件可以通过传递一个 props 来告诉每个 `Square` 显示什么,就像你将数字传递给每个 Square 时所做的那样。 -**要从多个子组件收集数据,或让两个子组件相互通信,请改为在其父组件中声明共享 state。父组件可以通过 prop 将该 state 传回给子组件。这使子组件彼此同步并与其父组件保持同步。** +**要从多个子组件收集数据,或让两个子组件相互通信,请改为在其父组件中声明共享 state。父组件可以通过 props 将该 state 传回给子组件。这使子组件彼此同步并与其父组件保持同步。** 重构 React 组件时,将状态提升到父组件中很常见。 @@ -945,7 +945,7 @@ export default function Board() { ['O', null, 'X', 'X', 'X', 'O', 'O', null, null] ``` -现在你的 `Board` 组件需要将 `value` prop 向下传递给它渲染的每个 `Square`: +现在你的 `Board` 组件需要将 `value` props 向下传递给它渲染的每个 `Square`: ```js {6-8,11-13,16-18} export default function Board() { @@ -972,7 +972,7 @@ export default function Board() { } ``` -接下来,你将编辑 `Square` 组件,以从 Board 组件接收 `value` prop。这将需要删除 Square 组件自己的 `value` state 和按钮的 `onClick` prop: +接下来,你将编辑 `Square` 组件,以从 Board 组件接收 `value` props。这将需要删除 Square 组件自己的 `value` state 和按钮的 `onClick` props: ```js {1,2} function Square({value}) { @@ -1066,7 +1066,7 @@ body { -现在,每个 Square 都会收到一个 `value` prop,对于空方块,该 prop 将是 `'X'`、`'O'` 或 `null`。 +现在,每个 Square 都会收到一个 `value` props,对于空方块,该 props 将是 `'X'`、`'O'` 或 `null`。 接下来,你需要更改单击 `Square` 时发生的情况。`Board` 组件现在维护已经填充过的方块。你需要为 `Square` 创建一种更新 `Board` state 的方法。由于 state 对于定义它的组件是私有的,因此你不能直接从 `Square` 更新 `Board` 的 state。 @@ -1082,7 +1082,7 @@ function Square({ value }) { } ``` -接下来,将 `onSquareClick` 函数添加到 `Square` 组件的 prop 中: +接下来,将 `onSquareClick` 函数添加到 `Square` 组件的 props 中: ```js {1} function Square({ value, onSquareClick }) { @@ -1094,7 +1094,7 @@ function Square({ value, onSquareClick }) { } ``` -现在,你将把 `onSquareClick` prop 连接到 `Board` 组件中的一个函数,命名为 `handleClick`。要将 `onSquareClick` 连接到 `handleClick`,需要将一个函数传递给第一个 `Square` 组件的 `onSquareClick` prop: +现在,你将把 `onSquareClick` props 连接到 `Board` 组件中的一个函数,命名为 `handleClick`。要将 `onSquareClick` 连接到 `handleClick`,需要将一个函数传递给第一个 `Square` 组件的 `onSquareClick` props: ```js {7} export default function Board() { @@ -1155,7 +1155,7 @@ export default function Board() { } ``` -接下来,你需要将 `i` 传递给 `handleClick`。你可以尝试像这样在 JSX 中直接将 square 的 `onSquareClick` prop 设置为 `handleClick(0)`,但这是行不通的: +接下来,你需要将 `i` 传递给 `handleClick`。你可以尝试像这样在 JSX 中直接将 square 的 `onSquareClick` props 设置为 `handleClick(0)`,但这是行不通的: ```jsx @@ -1171,9 +1171,9 @@ Too many re-renders. React limits the number of renders to prevent an infinite l 为什么这个问题没有早点发生? -当你传递 `onSquareClick={handleClick}` 时,你将 `handleClick` 函数作为 prop 向下传递。你不是在调用它!但是现在你立即调用了该函数——注意 `handleClick(0)` 中的括号——这就是它运行得太早的原因。你不想在用户点击之前调用 `handleClick` ! +当你传递 `onSquareClick={handleClick}` 时,你将 `handleClick` 函数作为 props 向下传递。你不是在调用它!但是现在你立即调用了该函数——注意 `handleClick(0)` 中的括号——这就是它运行得太早的原因。你不想在用户点击之前调用 `handleClick` ! -你可以通过创建调用 `handleClick(0)` 的函数(如 `handleFirstSquareClick`)、调用 `handleClick(1)` 的函数(如 `handleSecondSquareClick`)等来修复。你可以将这些函数作为 `onSquareClick={handleFirstSquareClick}` 之类的 prop 传递(而不是调用)。这将解决无限循环的问题。 +你可以通过创建调用 `handleClick(0)` 的函数(如 `handleFirstSquareClick`)、调用 `handleClick(1)` 的函数(如 `handleSecondSquareClick`)等来修复。你可以将这些函数作为 `onSquareClick={handleFirstSquareClick}` 之类的 props 传递(而不是调用)。这将解决无限循环的问题。 但是,定义九个不同的函数并为每个函数命名过于冗余。让我们这样做: @@ -1317,19 +1317,19 @@ body { -现在,我们在 `Board` 组件中处理 state, `Board` 父组件将 prop 传递给 `Square` 子组件,以便它们可以正确显示。单击 `Square` 时, `Square` 子组件现在要求 `Board` 父组件更新棋盘的 state。当 `Board` 的 state 改变时,`Board` 组件和每个子 `Square` 都会自动重新渲染。保存 `Board` 组件中所有方块的 state 将使得它可以确定未来的赢家。 +现在,我们在 `Board` 组件中处理 state, `Board` 父组件将 props 传递给 `Square` 子组件,以便它们可以正确显示。单击 `Square` 时, `Square` 子组件现在要求 `Board` 父组件更新棋盘的 state。当 `Board` 的 state 改变时,`Board` 组件和每个子 `Square` 都会自动重新渲染。保存 `Board` 组件中所有方块的 state 将使得它可以确定未来的赢家。 让我们回顾一下当用户单击你的棋盘左上角的方块以向其添加 `X` 时会发生什么: -1. 单击左上角的方块运行 `button` 从 `Square` 接收到的 `onClick` prop 的函数。`Square` 组件从 `Board` 通过 `onSquareClick` prop 接收到该函数。`Board` 组件直接在 JSX 中定义了该函数。它使用参数 `0` 调用 `handleClick`。 +1. 单击左上角的方块运行 `button` 从 `Square` 接收到的 `onClick` props 的函数。`Square` 组件从 `Board` 通过 `onSquareClick` props 接收到该函数。`Board` 组件直接在 JSX 中定义了该函数。它使用参数 `0` 调用 `handleClick`。 1. `handleClick` 使用参数(`0`)将 `squares` 数组的第一个元素从 `null` 更新为 `X`。 -1. `Board` 组件的 `squares` state 已更新,因此 `Board` 及其所有子组件都将重新渲染。这会导致索引为 `0` 的 `Square` 组件的 `value` prop 从 `null` 更改为 `X`。 +1. `Board` 组件的 `squares` state 已更新,因此 `Board` 及其所有子组件都将重新渲染。这会导致索引为 `0` 的 `Square` 组件的 `value` props 从 `null` 更改为 `X`。 最后,用户看到左上角的方块在单击后从空变为 `X`。 -DOM `