Open WangShuXian6 opened 1 day ago
要在 HTML 中实现一个 "Hello World",其实并不复杂。你只需要创建一个简单的 HTML 文档,使用 <body>
标签和一个包含 “Hello World” 的 <div>
或者 <p>
标签。
但如果你想通过 JavaScript 实现 "Hello World",也不会特别复杂。这是你第一次接触如何使用 JavaScript 与页面互动,并创建动态和交互式用户体验的入口。
要在页面上添加 JavaScript,可以使用 <script>
标签。你可以直接在这个标签内编写 JavaScript 代码,或者使用 src
属性链接到一个外部的 JavaScript 文件。虽然我们通常会将 JavaScript 代码放在单独的文件中,但为了简单起见,在初步学习中,我们会将所有内容放在一个文件里。
在这个练习中,我们的重点是学习以下内容:
可以通过访问 MDN DOM 文档 来了解更多关于 DOM 的信息。DOM 是从 JavaScript 在 Web 诞生之初就存在的一部分,通过 DOM,我们能够与页面进行交互。
尽管直接操作 DOM 可能有点混乱,但现代 JavaScript 和 DOM 的操作已经变得非常简洁高效。你可以使用这些技术构建一些不错的用户体验。当然,像 React 这样的框架提供了更多的功能,帮助我们更方便地进行复杂应用的开发,但在这个练习中,我们的重点是了解 DOM 的基础知识。
通过本次练习,你将学会使用 JavaScript 直接在 DOM 上实现 "Hello World",为之后深入学习 React 和其他高级工具打下坚实的基础。
在本次练习的第一步中,我们将使用 JavaScript 在 HTML 中创建一个 "Hello World"。当你完成后,页面应简单显示 "Hello World",如同示例中那样。
你可以按照以下步骤进行操作:
index.html
。<script>
标签,编写或引用 JavaScript 代码,将 "Hello World" 添加到页面。这是一项非常基础的练习,指引会帮助你完成,祝你玩得开心!
这个视频讲解了如何使用JavaScript和DOM API在HTML中创建一个“Hello World”的示例。以下是关键步骤的总结:
基本HTML结构:
div
元素的HTML文件。<script>
标签,并将type
设为module
,用来嵌入JavaScript代码。使用JavaScript操作DOM:
<script>
标签中,使用document.getElementById('root')
选择页面上的root元素。document.createElement('div')
创建一个新的div
元素。div
元素添加一个className
(例如:container
),并设置其textContent
为“Hello World”。appendChild()
或append()
将新创建的div
元素追加到root元素中。DOM操作:
通过这些步骤,你可以使用JavaScript和DOM操作技术,动态地在网页上创建并追加元素,实现“Hello World”的显示。
这段视频介绍了一个新的练习,目的是让你更深入地理解如何使用JavaScript操作DOM。与之前的练习不同,这次我们将完全用JavaScript生成根元素,而不是依赖于HTML预先定义的元素。以下是视频的主要内容:
挑战的目标:
<div>
),而不是在HTML中预先定义。任务内容:
document.createElement
动态生成一个根元素,并将其插入到文档中。动手实验:
练习的核心是完全通过JavaScript操控页面结构,摆脱对HTML的依赖,增强对动态DOM操作的理解。
在这段视频中,主要讲解了如何使用JavaScript动态创建并添加HTML元素到DOM中,以下是视频的核心内容总结:
删除现有元素:
cannot read properties of null reading append
”,因为在DOM中找不到该元素。使用JavaScript创建元素:
document.createElement
创建一个新的div
元素,并设置其id
为"root"。将新元素添加到DOM中:
document.body.append
方法,将动态创建的div
元素(rootElement
)添加到body
中,使其真正显示在页面上。document.body.prepend
,而不是append
。DOM的动态操作:
这段视频的目的是展示如何通过JavaScript操控DOM,创建并修改页面元素,帮助理解如何在动态网页中使用JavaScript进行基本的DOM操作。
在这段视频中,介绍了一个轻松的休息时间。讲者强调了在学习过程中定期休息的重要性,帮助大脑吸收和巩固所学内容。视频还分享了一个轻松的笑话:
之后,讲者提醒观众保持充足的水分,并鼓励大家利用这段时间稍作休息,为接下来的学习做好准备。
这段视频介绍了如何在引入React的基础上,通过不使用JSX的方式,使用原生React API创建一个简单的"Hello World"应用。讲者首先解释了React是基于传统的document.createElement
构建的,目的是让开发者理解React背后的工作原理。以下是视频中的一些关键点:
React与React DOM:讲者介绍了React不仅仅是一个单一的包,它实际上分为两个主要的包:React
和React DOM
。React
负责管理组件、hooks和API,而React DOM
则负责将React组件渲染到网页的DOM上。
React的跨平台能力:除了网页上的React渲染器(React DOM),React还可以用于虚拟现实、原生桌面应用和命令行界面等场景。
从原生DOM到React的转换:通过引入React和React DOM,开发者能够将React元素转换为可以在网页上显示的DOM元素。
视频的目的是引导开发者通过逐步学习React的底层API,最终理解React如何简化开发工作,并逐步深入React的使用。
在这个练习中,我们将React和React DOM引入页面,并使用它们的createElement
和createRoot
API来替代原生的document.createElement
等操作。讲者提醒,这并不是通常引入React的方式,通常你会使用构建工具(如Webpack、Parcel)来处理React的构建和优化工作,包括类型检查、打包和性能优化等。
在这个简单的练习中,我们的目标是通过React的声明式API(如React.createElement
)来实现之前使用原生DOM API的效果。尽管最后页面的展示效果与之前完全相同,但我们将会使用React来构建整个页面结构。这是从传统DOM操作向React世界过渡的第一步。
你可以通过查看项目仓库中的公共目录来了解如何加载React和React DOM库,并使用它们来创建页面元素。希望你在这次练习中有所收获!
在这个示例中,我们展示了如何使用React和React DOM将元素渲染到页面上。首先,我们通过createElement
从React创建一个新的元素,并设置className
和children
属性来替代传统的class
和textContent
。
React元素只是一个UI描述符,它不是直接的DOM元素。因此,我们还需要通过React DOM中的createRoot
API来将这个React元素渲染为实际的DOM元素。以下是我们所做的关键步骤:
createElement
:我们用React的createElement
来创建一个元素。在这个例子中,我们创建了一个div
,并给它设置了className
和children
属性。createRoot
渲染元素:通过createRoot
API,我们将这个元素附加到页面上已经存在的根节点rootElement
。然后通过render
方法将React元素实际渲染到DOM中。最后的效果是在页面上显示一个带有"Hello World"文本的div
元素。
这是React基本工作原理的一个很好的演示,展示了从UI描述符(React元素)到实际DOM渲染的流程。
在这个练习中,我们将处理多个子元素并确保它们之间的空格正确显示。具体来说,我们会在一个React元素中创建两个span
标签,一个用于显示"Hello",另一个用于显示"World",并确保它们之间有一个空格。
span
元素,一个显示"Hello",另一个显示"World"。React允许我们将多个子元素传递给一个父元素,而不仅限于单个子元素。要实现这个需求,我们可以通过创建两个span
元素并在它们之间添加一个空格或字符串来实现。
示例代码如下:
import React from 'react';
import ReactDOM from 'react-dom/client';
const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);
const element = React.createElement(
'div',
null,
React.createElement('span', null, 'Hello'),
' ', // 这是我们用来添加空格的部分
React.createElement('span', null, 'World')
);
root.render(element);
React.createElement
: 使用该方法分别创建两个span
元素,分别包含"Hello"和"World"。span
元素之间直接添加一个字符串' '
(一个空格字符),确保在它们之间有空格。最终效果是页面上显示"Hello World",并且"Hello"和"World"之间有一个空格。
这个练习展示了如何在React中使用多个子元素,并处理它们之间的布局问题。
在这个练习中,我们使用 React 的 createElement
API 来构建嵌套的 UI,并处理多个子元素。这次的目标是创建两个 span
元素,一个显示“Hello”,另一个显示“World”,并确保它们之间有空格。
使用 React.createElement
创建 span
元素:
React.createElement('span', null, 'Hello')
创建第一个 span
,内容为“Hello”。span
,内容为“World”。在 span
元素之间添加空格:
' '
,React 将确保这个空格被渲染为文本节点,从而在两个 span
元素之间显示空格。将所有元素作为子元素传递:
React.createElement
的第三个参数开始表示子元素,可以是文本、元素或数组形式。在这个例子中,我们通过传递多个子元素来构建 div
标签的内容。以下是完整代码:
import React from 'react';
import ReactDOM from 'react-dom/client';
const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);
const element = React.createElement(
'div',
null,
React.createElement('span', null, 'Hello'),
' ', // 这是用来添加空格的字符串
React.createElement('span', null, 'World')
);
root.render(element);
React.createElement('span', null, 'Hello')
: 创建一个 span
元素,内容为 "Hello"。' '
: 这是一个空格字符串,它被作为子元素传递,使两个 span
元素之间有一个空格。React.createElement('span', null, 'World')
: 创建第二个 span
元素,内容为 "World"。' '
来表示空格。React.createElement
接受多个子元素作为第三个及后续参数。它们会一起渲染到父元素中。通过这种方式,我们可以确保在页面上正确显示“Hello World”,并且“Hello”和“World”之间有一个空格。
在这个练习中,我们将使用 React.createElement
来构建一个更加复杂的嵌套结构。最终的目标是创建一个包含 p
标签、一个 ul
列表及其多个 li
项的结构。当我们完成时,DOM 树应看起来如下:
<div class="container">
<p>Some text here</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
createElement
构建整个嵌套结构。import React from 'react';
import ReactDOM from 'react-dom/client';
const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);
// 创建包含嵌套元素的结构
const element = React.createElement(
'div',
{ className: 'container' }, // 父 div 容器
React.createElement('p', null, 'Some text here'), // p 标签
React.createElement(
'ul',
null,
React.createElement('li', null, 'Item 1'), // li 项目1
React.createElement('li', null, 'Item 2'), // li 项目2
React.createElement('li', null, 'Item 3') // li 项目3
)
);
// 渲染到页面
root.render(element);
顶层 div
元素:
React.createElement('div', { className: 'container' }, ...)
创建一个 div
,并赋予 className
为 "container"。p
标签:
React.createElement('p', null, 'Some text here')
创建一个 p
标签,包含文本内容 "Some text here"。ul
列表和 li
项目:
React.createElement('ul', null, ...)
创建一个 ul
列表,并在其中嵌套多个 li
项目。嵌套结构:
ul
列表的每个 li
项是通过调用 React.createElement('li', null, 'Item X')
创建的,X
代表不同的项目编号。虽然这个结构不算特别复杂,但通过这种方式嵌套多个 createElement
调用,可以让我们深刻体会到 JSX 的优势。直接使用 createElement
来构建复杂的 UI 结构时,代码的可读性会变差,层次感也不明显。而在 JSX 中,嵌套结构可以以更接近 HTML 的方式书写,既直观又高效。
这个练习让我们理解 React.createElement
的灵活性和强大之处,同时也预示了 JSX 如何简化开发体验。
在这个练习中,我们使用了 React.createElement
来创建更复杂的嵌套 UI 结构。与之前的 Hello World
相比,我们现在正在构建一个包含 <p>
标签、<ul>
列表和多个 <li>
项的结构。这个结构展示了 React 元素嵌套的工作方式,但同时也让我们理解到 JSX 在处理大量嵌套时的优势。
import React from 'react';
import ReactDOM from 'react-dom/client';
// 获取 root 元素
const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);
// 使用 createElement 创建嵌套的 UI 结构
const element = React.createElement(
'div',
{ className: 'container' },
React.createElement('p', null, "Sam's favorite food"), // p 标签
React.createElement(
'ul',
{ className: 'sams-food' }, // ul 列表,带有 className 属性
React.createElement('li', null, 'Green eggs'), // li 项目1
React.createElement('li', null, 'Ham') // li 项目2
)
);
// 渲染到页面
root.render(element);
p
标签:
React.createElement('p', null, "Sam's favorite food")
创建一个 p
标签,显示文本内容为 "Sam's favorite food"。ul
列表:
React.createElement('ul', { className: 'sams-food' }, ...)
创建一个带有 className
为 sams-food
的 ul
列表,并在其中嵌套多个 li
项。li
项目:
React.createElement('li', null, 'Green eggs')
和 React.createElement('li', null, 'Ham')
分别创建两个列表项,显示 "Green eggs" 和 "Ham"。虽然 React.createElement
提供了创建复杂 UI 的能力,但随着嵌套层级增加,代码的可读性和维护性可能会变差。这就是 JSX 存在的主要原因:它让我们用类似 HTML 的语法来编写 React 元素,大大提高了代码的可读性和简洁性。
通过这个练习,你已经学会了如何使用 React.createElement
创建嵌套的 UI 结构,同时也体验到当 UI 复杂度增加时,JSX 的优势会变得非常明显。
哈哈,那个笑话真是让人忍俊不禁!抓住一个放了一个,真是个有趣的双关。希望这个小休息能让你放松一下,记得保持水分和适当休息哦!当你准备好了,我们会继续学习更多的 React 知识,深入探索这个强大的框架,掌握更多有趣的技巧!
JSX 确实为 React 编写用户界面带来了很大的方便,它使得我们可以以接近 HTML 的语法编写代码,而不必通过复杂的 createElement
API 一层层嵌套调用。
React 团队创建了 JSX 语法,它本质上是一种让你在 JavaScript 中写 XML 类似的语法,JSX 然后会通过编译器(如 Babel)转换为 React 的 createElement
调用。虽然浏览器本身不理解 JSX 语法,但是通过像 Babel 这样的工具,JSX 可以被编译成常规的 JavaScript,从而在浏览器中运行。
为了保持事情简单,React 的 JSX 编译器 Babel 可以在浏览器中直接运行,因此我们可以避免使用复杂的构建工具。在本次练习中,你将学习如何在浏览器中通过引入 Babel 来编写 JSX 语法,并了解 JSX 是如何转换为 React.createElement
调用的。
在这项练习中,你将体验到 JSX 的强大和简洁性,让编写 UI 变得更加高效流畅。准备好了吗?让我们一起开始学习 JSX 吧!
在这个练习中,我们将把 Babel 添加到页面中,并将 createElement
调用转换为 JSX 语法。
首先,我们需要在页面中引入 Babel,这次我们会使用 Babel 的单独版本(Babel standalone),它是一个打包好的单个脚本,可以直接在浏览器中加载并执行。通过这个脚本,Babel 会在页面中查找具有特定 type
类型的 <script>
标签,并对其内容进行编译,最终生成新的脚本标签让浏览器评估执行。
具体步骤如下:
加载 Babel standalone:
<script>
标签将其引入页面。更新 <script>
标签:
<script>
标签的 type
属性设置为 "text/babel"
,这会告诉 Babel,它应该编译这个脚本中的 JSX 内容。转换 JSX:
createElement
API 替换为 JSX 语法。Babel 会自动将 JSX 转换为对应的 React.createElement
调用。这个过程让你能够直观地了解 JSX 如何转换为常规的 JavaScript 调用,虽然这种方式不常用于生产环境,但它能帮助你在没有构建工具的情况下运行 React 项目。
祝你练习愉快!
在这个练习中,我们已经成功地将 Babel 添加到页面中,并且将 createElement
调用转换为了 JSX 语法。通过这个过程,我们学到了几个关键点:
添加 Babel: Babel 是一个编译器,可以将 JSX 代码转换为 JavaScript 代码,让浏览器能够理解和执行。虽然我们在练习中加载了一个较大的 Babel standalone 文件,这种方法不适合生产环境,但它适合我们进行开发和学习。
使用 JSX 替代 createElement
调用:
我们将 JSX 替换了原本的 React.createElement
调用。JSX 语法更接近 HTML,简洁直观,可以更容易地编写 React 组件。
Babel 编译:
Babel 会自动查找类型为 text/babel
的 <script>
标签,编译其中的内容,并将其转化为浏览器可以执行的 JavaScript 代码。在生产环境中,你通常会使用编译工具(如 Webpack 等)来实现这一过程。
模块导入:
Babel 编译后的代码依赖于 React.createElement
,所以我们需要确保在代码中引入 React。通过 import * as React from 'react';
,我们可以确保 React 被正确导入和引用。
JSX 的好处:
使用 JSX 语法,让我们可以轻松地嵌套和构建复杂的 UI 元素,而不必手动编写繁琐的 createElement
调用。这样可以大大提高开发效率和代码的可读性。
接下来,我们可以继续探索 JSX 的更多特性和强大之处,并且通过这些练习加深对 React 的理解。
在这个练习中,我们讨论了插值(Interpolation)的概念,特别是在 JSX 中的应用。在 React 和 JSX 中,插值是将 JavaScript 代码嵌入到 JSX 表达式中的一种方式,这与在模板字符串(Template Literal)中的插值非常类似。
插值的概念:
插值的核心思想是允许在字符串或 JSX 中嵌入 JavaScript 表达式。通过这种方式,我们可以动态地生成内容。例如,使用反引号 (`
) 和 ${}
在模板字符串中嵌入变量或表达式,这与在 JSX 中使用花括号 {}
插入 JavaScript 表达式类似。
JSX 中的插值:
在 JSX 中,你可以通过花括号 {}
在 HTML 标签的属性和内容中嵌入 JavaScript 表达式。例如:
const name = "React";
return <div className={`container ${name}`}>Hello, {name}!</div>;
在这个例子中,className
和 div
的内容都使用了插值,将 JavaScript 表达式插入 JSX。
插值位置:
className={classNameVariable}
。<div>{message}</div>
,其中 message
是一个变量。JSX 和 JavaScript 模式的切换:
在 JSX 中使用 {}
插入 JavaScript 表达式相当于在普通 JavaScript 中使用 ${}
插入变量或表达式。花括号 {}
是一种告诉 JSX 解析器 "进入 JavaScript 模式" 的方式,解析器会解析 {}
中的表达式,然后返回其结果并插入到 DOM 中。
插值是 React 和 JSX 的重要特性,它使得我们可以轻松地将动态数据插入到组件中,使得我们的 UI 更加灵活和可复用。在接下来的练习中,你将能够实践这些插值的技巧,从而进一步掌握它们的应用。
在这个练习中,我们讨论了如何在 JSX 中进行插值(Interpolation),特别是在 JSX 和 JavaScript 之间切换的方式。
JSX 与 JavaScript 的切换:
在 JSX 中,通过使用 {}
可以将 JavaScript 表达式嵌入到 JSX 中。当 JSX 编译时,任何在 {}
中的内容都会被当作 JavaScript 表达式进行评估,并插入到对应的位置。例如:
const className = "container";
const children = "Hello World";
return <div className={className}>{children}</div>;
这里的 className
和 children
都是通过插值的方式传递给 JSX 元素。
插值的语法:
className={className}
。<div>{children}</div>
。切换状态:
<div></div>
等 JSX 语法时,React 正在解析类似于 HTML 的代码。{}
包裹表达式时,解析器会将其转换为 JavaScript 代码,执行后插入结果。自闭合标签:
在 JSX 中可以使用自闭合标签,例如 <img />
或 <input />
,这是 JSX 的一项简化功能,它允许你在没有子元素时不必书写结束标签。
表达式与逻辑:
if
或 for
语句,但可以使用三元运算符来控制逻辑:
{isTrue ? <p>True</p> : <p>False</p>}
通过这些概念的掌握,你将能够更好地在 JSX 中处理动态内容和属性,使得你的 React 应用更加灵活和强大。
在这个练习中,我们介绍了如何将一个包含多个属性的对象应用到 JSX 元素上,而无需单独为每个属性进行手动设置。你可能会经常遇到这种情况,尤其是在属性动态生成或者属性数量较多的场景下。
假设我们有一个 props
对象,其中包含了 className
和 children
等属性。常规的做法是这样写:
const props = {
className: "container",
children: "Hello World",
};
return (
<div className={props.className}>
{props.children}
</div>
);
这需要你逐个指定对象中的属性,例如 className={props.className}
,children={props.children}
。
为了避免手动传递每个属性,JSX 提供了一种更简洁的方法,即 属性扩展语法,类似于 JavaScript 中的 扩展操作符。你可以通过 ...props
将对象中的所有属性应用到 JSX 元素上:
const props = {
className: "container",
children: "Hello World",
};
return (
<div {...props} />
);
...props
会将 props
对象中的每个键值对分别扩展为该元素的属性。上面的代码与以下手动传递属性的代码是等效的:
<div className="container">Hello World</div>
这种写法特别适合以下场景:
通过使用这种方式,你可以更轻松地处理具有多个属性的 JSX 元素。
在这个练习中,我们探讨了如何在 JSX 中使用扩展语法将一个对象中的所有属性应用到某个元素上。这种方式对于处理动态属性或者复杂的属性集合时非常有用。
我们可以使用 ...props
将一个对象中的所有属性“展开”并应用到 JSX 元素上。例如:
const props = {
className: "container",
children: "Hello World",
};
return <div {...props} />;
这段代码会将 props
对象中的 className
和 children
属性直接应用到 div
元素上,等效于手动指定这些属性:
<div className="container">Hello World</div>
扩展操作符的一个关键点是属性覆盖。如果你在展开属性对象后又指定了同名的属性,那么 JSX 会采用后面定义的属性值。例如:
const props = {
className: "container",
children: "Hello World",
};
return <div {...props} className="my-container" />;
在这个例子中,className="my-container"
会覆盖 props
中的 className="container"
,因为它出现在 ...props
之后。
输出的结果是:
<div class="my-container">Hello World</div>
children
属性children
属性有点特殊。如果你直接在元素标签之间定义了内容,例如:
return <div {...props}>Goodbye World</div>;
此时,"Goodbye World"
会覆盖 props
对象中的 children
属性。换句话说,标签内部的内容优先级更高。
你可以将多个属性对象进行扩展,并且扩展顺序决定了最终应用的属性。例如:
const props1 = { className: "container" };
const props2 = { className: "my-container" };
return <div {...props1} {...props2} />;
在这个例子中,props2
中的 className="my-container"
会覆盖 props1
中的 className="container"
,因为 props2
位于 props1
之后。
输出的结果是:
<div class="my-container"></div>
通过这种方式,你可以灵活地管理和覆盖 JSX 元素的属性,同时减少手动编写重复代码的麻烦。
在这个练习中,我们将把之前使用 createElement
创建的 "Sam's Favorite Food" 列表(包含绿色鸡蛋和火腿)转化为使用 JSX 编写的版本。这将展示 JSX 在处理嵌套结构时的简洁性和可读性。
在 createElement
中,我们需要通过多层函数调用来构建嵌套的 HTML 结构。而使用 JSX 时,编写这样的嵌套结构会更加直观和接近原生的 HTML。以下是我们如何使用 JSX 来重现 "Sam's Favorite Food" 列表的代码:
function FavoriteFood() {
return (
<div className="container">
<p>Sam's favorite food:</p>
<ul className="sam-food">
<li>Green Eggs</li>
<li>Ham</li>
</ul>
</div>
);
}
createElement
的区别与 createElement
的多层函数调用不同,JSX 更加贴近 HTML 的语法,并且在可读性和直观性上有显著提高:
createElement
,让代码变得更加简洁易懂。class
属性被改为 className
,因为 class
是 JavaScript 中的保留字。<img />
、<br />
这样的单标签元素需要自闭合。通过这种方式,JSX 让我们更轻松地处理复杂的嵌套结构,尤其是在大型应用程序中,它可以极大地简化代码的编写与维护。
在这个片段中,讲解了如何通过将 HTML 代码直接复制粘贴到 JSX 中,并且展示了在 JSX 中处理嵌套结构是多么简单和高效。然而,JSX 与 HTML 之间有一些重要的区别,尤其是关于属性的命名。例如:
class
与 className
的区别:在 JSX 中,我们不能像在 HTML 中那样使用 class
属性,因为 class
在 JavaScript 中是一个保留字。在 JSX 中,你需要使用 className
来指定 CSS 类名。这是因为 JSX 更关注的是 DOM 属性,而不是 HTML 属性。
HTML 属性 vs. DOM 属性:JSX 使用的是 DOM 属性,而不是 HTML 属性。例如,表单中的 for
属性在 JSX 中应该写作 htmlFor
。这种转换有助于避免与 JavaScript 保留字的冲突,并保持一致性。
改进后的编程体验:通过 JSX,我们不再需要使用 React.createElement
来手动创建和嵌套元素。JSX 更加直观和简洁,类似于 HTML,这使得编写复杂的用户界面变得更加容易。
使用 JSX 明显提高了代码的可读性和开发效率,特别是在处理复杂的嵌套结构时。虽然 JSX 和 HTML 之间存在一些细微的差异(如属性命名),但这都是为了与 JavaScript 保持一致,并优化开发体验。
在这一段中,讲解了如何使用 React Fragments 来避免不必要的包裹元素,尤其是在某些布局需求(如 CSS Grid 或 Flexbox)中可能非常有用。
通常情况下,React 元素必须被一个单一的父元素包裹。这意味着如果你想返回多个兄弟元素(而不希望它们被一个 div
包裹),你可以使用 React Fragments 来实现。Fragments 允许你返回多个元素,而不在 DOM 中添加额外的包裹元素。
标准写法:
<React.Fragment>
<Element1 />
<Element2 />
</React.Fragment>
或者你可以先导入 Fragment
:
import { Fragment } from 'react';
然后:
<Fragment>
<Element1 />
<Element2 />
</Fragment>
简洁写法:
你可以用简写的形式,省略 Fragment
的名字,只需使用空标签包裹内容:
<>
<Element1 />
<Element2 />
</>
div
或其他容器标签中。通过使用 Fragment,你可以确保只输出你需要的元素,而不会在 DOM 中添加额外的节点,这对于保持 DOM 结构简洁和避免不必要的布局影响非常有帮助。
通过使用 React Fragments,你可以避免多余的容器元素,同时保持 JSX 代码的简洁和清晰。这种技巧特别适合处理复杂的布局结构,或者在组件返回多个元素时更好地控制 DOM 结构。
这一段解释了 React Fragments 的使用,并展示了为什么需要使用它来避免不必要的 div
包裹,同时还深入讲解了为何在 JavaScript 中无法直接返回多个顶层元素。
问题的来源:
div
和一个 ul
,会导致编译错误,因为 JavaScript 无法让一个变量同时指向两个不同的值。为什么不能直接移除父元素:
div
),React 会抛出错误,因为它无法理解如何将多个兄弟元素直接渲染在 DOM 中。JavaScript 不支持给一个变量赋值多个值,这就是为什么必须有一个父元素包裹它们。React Fragments 的引入:
Fragment
,它是一个 "虚拟" 的容器,允许你返回多个元素,但不会在 DOM 中生成任何实际的包裹元素。div
或者 span
,但仍然需要满足 React 的语法要求时。Fragment 的两种写法:
<React.Fragment>
<Element1 />
<Element2 />
</React.Fragment>
<>
<Element1 />
<Element2 />
</>
通过使用 React Fragments,你可以在不生成多余 DOM 元素的情况下返回多个兄弟元素,这使得代码更简洁,同时也避免了多余的嵌套层级。它非常适合在 CSS 布局或组件返回多个元素时使用。
哈哈!这个笑话很轻松有趣:“什么样的贝果会飞?答案是——普通贝果(plane bagel)!” 这也是个非常好的提示,让我们放松一下、站起来活动活动。
现在正是时候伸展一下身体,去喝点水或者吃个小零食。如果有机会,还可以去对别人说点鼓励的话,帮助别人也会让自己感觉很好。之后回来继续学习更多有趣的 React 内容!
在这个练习中,我们开始了解自定义组件的概念。在 React 中,自定义组件其实很简单,它就是一个函数,该函数接受一个对象(通常是 props
),并返回一些可以渲染的内容。那就是自定义组件的全部定义。
通常,返回的内容是 React 元素,但实际上它也可以是一个字符串、数字等可渲染的值。从本质上讲,自定义组件是你可以传递给 createElement
API 的东西。在 JSX 中,自定义组件有专门的语法,可以让你轻松地在 JSX 中使用这些自定义组件。
这里有一个简单的例子:一个名为 greeting
的函数组件,它接收 props
对象,然后这些 props
就是你渲染该组件时传递的值。
在接下来的练习中,你会逐步通过这些步骤来熟悉自定义组件的创建和使用,学习 JSX 是如何将这些组件编译成实际的函数调用,并了解 React 是如何处理这些调用的。
这个过程很有趣,也非常有用。希望你在接下来的练习中玩得开心,祝你好运!
在这个练习的第一部分,我们将逐步靠近创建一个可以生成可重用 JSX 的通用函数。现在我们有一个容器,它里面有两个消息,分别是“hello world”和“goodbye world”。显然,这里有一些重复的代码,我们可以优化它。
为了使代码更加通用,你可以编写一个函数,这个函数可以接收动态的子元素 children
,从而减少重复。例如,你可以创建一个名为 message
的函数,它允许你传递不同的 children
内容。
这个练习的目标是让你实现这样一个通用的函数接口。虽然这还不是一个完整的 React 组件,但我们正在逐渐靠近 React 组件的形式。通过插值(interpolation),我们可以将函数调用的结果作为表达式插入 JSX 中,而函数返回的 React 元素可以作为 div
的子元素渲染。
任务:你需要编写这个通用的 message
函数,并通过插值将它的输出作为 JSX 的一部分。
祝你在这个过程中玩得愉快,完成之后我们会继续!
在这个步骤中,我们通过创建一个 message
函数来减少代码重复。这个函数接收一个对象作为参数,并将其子元素 children
渲染到指定位置。最终,我们可以调用这个函数,传递需要显示的内容,从而避免代码的重复。
通过使用这个 message
函数,如果你想要在多个地方更新样式或结构,你只需要修改函数内部,而不必手动修改每个使用的地方。这就是减少代码重复的优势。
这个练习让我们更接近于 React 组件的结构,虽然当前的写法还不是完全的 React 组件形式,但它展示了如何通过参数化来灵活地处理 JSX 的渲染。在下一步中,我们将进一步探索真正的 React 组件并逐步优化这一过程。
祝你在这个过程愉快!
在这个步骤中,我们将进一步优化代码,使其更加符合 React 组件的语义,特别是通过使用 CreateElement
API。
我们需要将自定义的 message
函数转化为真正的 React 组件,并通过 CreateElement
API 来处理它,而不是直接调用函数。通过这种方式,React 将能够更好地管理组件的生命周期,比如何时渲染或更新组件。
组件的定义:
你仍然会保留 message
函数,但是不再直接调用它。相反,你会将这个函数作为一个特殊的元素传递给 React.createElement
。这样 React 就会负责调用这个函数,而不是你手动调用它。
传递 props:
在 React 中,当你通过 CreateElement
API 创建一个元素时,所有的 props 都会被组合成一个对象并传递给组件。我们会把 children
作为这个 props 的一部分。
Render 流程:
通过 React.createElement
API 传递 message
函数,然后由 React 来调用这个函数,并将 props 传递给它。
function Message({ children }) {
console.log('Rendering Message component');
return <div className="message">{children}</div>;
}
// 使用 CreateElement API
const element = React.createElement(Message, null, 'Hello World');
// Render 到 DOM
ReactDOM.createRoot(document.getElementById('root')).render(element);
在这个例子中,我们定义了一个 Message
组件,它接受一个 children
prop 并渲染它。然后,我们使用 React.createElement
来创建这个组件的实例,而不是手动调用它。React 会自动处理组件的调用并传递 props。
通过这种方法,当你引入更多复杂的功能,比如 hooks 时,你会发现 React 组件的这种形式非常重要和方便。
你可以通过在 Message
函数和 React.createElement
的各个位置添加 console.log
,来观察 React 是如何管理组件的渲染的。
React.createElement
是生成 React 元素的 API。CreateElement
API,React 将自动管理组件的渲染、更新等操作。希望你在这个过程中愉快探索!
在这个步骤中,我们通过使用 React.createElement
进一步加深了对 React 组件的理解,并学习了如何让 React 自己决定何时调用我们的自定义组件。通过这种方式,我们的组件更符合 React 的生命周期和渲染机制。
React.createElement
调用:
我们将 message
函数作为自定义组件传递给 React.createElement
,并让 React 负责在渲染时调用该组件。React 会传递 props,并在必要时调用组件函数。
延迟调用:
使用 React.createElement
后,React 将延迟调用组件,直到真正需要渲染时才调用组件。这与我们直接调用组件函数的方式不同。之前的方式是立即调用函数,而现在是由 React 自己来控制。
React 特有的元素类型:
当我们使用 React.createElement
时,React 创建了一个特殊的 React 元素,其类型不是普通的 HTML 标签,而是我们的自定义组件。这为组件的扩展和复用提供了可能性。
生命周期:
在我们继续深入理解 React 的状态管理和生命周期方法时,使用 React.createElement
这种方式的优势会变得更加明显。React 可以更高效地管理组件的渲染和更新,同时保持应用的状态隔离和一致性。
通过这一步,我们的组件已经在语义上符合了 React 的工作方式,即 React 自己负责组件的调用和管理。而接下来我们将进一步完善 JSX 语法,使自定义组件的使用更加简洁和直观。
希望这个过程帮助你更好地理解了 React.createElement
的机制和自定义组件的工作原理。
在这个练习中,我们的目标是从使用 React.createElement
过渡到更简洁、易读的 JSX 语法。不过需要注意的是:自定义组件的名称大小写非常重要。
在使用 JSX 时,React 会将小写字母开头的名称(例如 div
、span
)视为 HTML 标签。如果你使用了小写的自定义组件名称(例如 message
),React 会将它当作一个字符串,认为它是一个 HTML 标签。然而,当你将名称首字母大写(例如 Message
)时,React 就知道这是一个函数引用(自定义组件)。
通过这个过程,你将理解 React 如何编译 JSX 以及它如何区分 HTML 元素和自定义组件。
不大写组件名称: 首先,将你的自定义组件名称写成小写形式,并查看 React 如何将其编译为字符串:
const element = (
<div>
<message>Hello World</message>
</div>
);
将名称首字母大写: 现在,将组件名称首字母大写,查看编译后的不同:
const element = (
<div>
<Message>Hello World</Message>
</div>
);
当你将名称大写时,React 会识别 Message
是一个自定义组件,而不是一个 HTML 标签,并会调用函数,而不是将其当作字符串标签名处理。
这是以后你在 JSX 中编写 React 组件的标准方式,它比 React.createElement
提供了更清晰、可读性更高的语法。
一开始,我们会将 React.createElement
替换为 JSX 语法。让我们把 message
标签应用到 JSX 中:
<Message>Hello World</Message>
<Message>Goodbye World</Message>
表面上看,这似乎是有效的,但我们会得到一个警告:“浏览器无法识别标签 message
”。这意味着 JSX 正在尝试将 message
作为一个 HTML 标签,而不是 React 组件。React 提示我们:如果要渲染一个 React 组件,请将名称的首字母大写。
为了让 React 识别这是一个自定义组件,而不是原生 DOM 元素,我们需要将自定义组件的名称首字母大写:
<Message>Hello World</Message>
<Message>Goodbye World</Message>
当我们查看编译后的输出时,原来使用小写 message
时,React 编译器会将其解释为字符串 'message'
,即浏览器尝试渲染一个不存在的原生 DOM 元素。而当我们将其大写为 Message
后,React 就能识别这是一个函数引用,并调用对应的 React 组件。
通过这种方式,你可以轻松地创建并使用自定义组件,并遵循 React 的约定使代码更加规范和清晰。
React 使用组件名称的大小写来区分 HTML 元素和自定义组件。因此,当定义自定义组件时,始终记得将其名称首字母大写,以确保 JSX 能够正确编译并渲染组件。
这个练习是为了加深你对自定义组件和 props
的理解。我们将创建一个简单的计算器组件,这个组件将接收 left
、operator
和 right
作为属性,并渲染一个包含计算结果的 div
。
你需要实现一个 Calculator
组件,它会:
left
(左操作数)、operator
(运算符)和 right
(右操作数)作为 props
。props
生成一个计算表达式,比如:3 + 5
。下面是一个简单的实现思路:
function Calculator({ left, operator, right }) {
let result;
switch (operator) {
case '+':
result = left + right;
break;
case '-':
result = left - right;
break;
case '*':
result = left * right;
break;
case '/':
result = right !== 0 ? left / right : 'Error';
break;
default:
result = 'Invalid operator';
}
return (
<div>
{left} {operator} {right} = {result}
</div>
);
}
// 使用示例
<Calculator left={3} operator="+" right={5} />
<Calculator left={10} operator="*" right={2} />
Calculator
组件使用 props
来接收 left
、operator
和 right
。operator
的值,组件会执行相应的数学运算。left
、operator
、right
和结果渲染到页面上。尝试使用不同的 props
调用 Calculator
组件,并查看其工作效果。这个练习旨在帮助你理解如何通过 props
在 React 中传递数据并创建动态组件。
在这个练习中,我们创建了一个 Calculator
组件,并通过解构 props
来获取 left
、operator
和 right
三个参数。然后根据这些参数执行运算,并展示结果。
解构 props
:
我们从传入的 props
对象中解构出 left
、operator
和 right
。这种方式可以让代码更加简洁和可读。
执行运算:
使用 operator
来决定执行哪种运算(加、减、乘、除),并将结果存储在 result
变量中。这个步骤模拟了简单的数学运算。
渲染结果:
我们使用 JSX 来将 left
、operator
和 right
的值显示出来,并将计算结果显示在 output
标签中,以便于提高无障碍性。
function Calculator({ left, operator, right }) {
let result;
switch (operator) {
case '+':
result = left + right;
break;
case '-':
result = left - right;
break;
case '*':
result = left * right;
break;
case '/':
result = right !== 0 ? left / right : 'Error';
break;
default:
result = 'Invalid operator';
}
return (
<div>
<code>{left} {operator} {right}</code> = <output>{result}</output>
</div>
);
}
// 使用示例
<Calculator left={1} operator="+" right={2} />
<Calculator left={1} operator="/" right={2} />
灵活的 props
:
组件的 props
可以是任意类型,不仅限于原始类型(如数字、字符串),还可以是对象或其他 React 元素。
JSX 语法:
在 JSX 中,通过 {}
可以插入 JavaScript 表达式来显示变量或计算结果。
组件本质:
React 组件本质上是一个函数,它接受 props
作为参数,并返回需要渲染的 JSX。
通过这个练习,你可以进一步熟悉如何使用 props
传递数据,并在 React 组件中灵活渲染动态内容。
哈哈,Michael Jackson 的笑话和 React Router 的双关真是有趣!看来你学到很多 React 的知识,确实是时候让这些内容在大脑中“炖一炖”了!趁着这个休息时间,站起来活动一下,让身体和大脑都焕然一新。
在你休息之后,我们还会继续深入学习 React 的精彩部分。我很期待你回来,咱们继续一起学习 React 的其他酷炫内容!
从现在开始,我们将使用 TypeScript 来完成所有的开发。TypeScript 是一种建立在 JavaScript 之上的强类型语言,它可以为你带来类型安全性。
类型安全的最大好处就是,当别人调用你的函数时,如果传递的参数类型不对,TypeScript 会在编译时给你报错。比如,如果一个函数需要传入字符串(因为可能会用到 .toUpperCase()
这样的操作),但你传入了一个数字,TypeScript 就会提前告知你。在普通的 JavaScript 中,这是不会被捕获的。
整个行业目前基本都在使用 TypeScript,你也肯定想跟上这个趋势。接下来我们将开始在这个练习中探索 TypeScript。
有些人可能会觉得 TypeScript 会给他们的代码带来额外的“红色波浪线”,但其实这些都是帮助你避免潜在错误的警告。可以把 TypeScript 想象成一个“无情但诚实的朋友”,虽然它会指出代码中的问题,但它的目的是为了避免你犯下严重的错误。
React 组件本质上是接受对象(props)的函数,并返回可以渲染的东西。从 TypeScript 的角度来看,你只需要为这些函数定义类型。没有什么特别的规则,就是为函数加上类型定义。
这是一个普通的 JavaScript 函数:
function getUserDisplayName(user) {
return user.name || 'Unknown';
}
如果我们用 TypeScript,可以这样定义类型:
function getUserDisplayName(user: { name?: string }): string {
return user.name || 'Unknown';
}
上面的 user
参数必须是一个对象,并且可能有一个可选的 name
属性,类型为 string
。TypeScript 会帮你确保返回的结果始终是一个 string
。
下面是一个没有类型定义的 React 组件:
const Message = ({ children }) => <div>{children}</div>;
我们可以使用 TypeScript 为 children
添加类型:
const Message: React.FC<{ children: React.ReactNode }> = ({ children }) => {
return <div>{children}</div>;
};
这样,children
可以是任何 React 可渲染的内容,比如字符串、元素或其它组件。
如果你刚开始使用 TypeScript,可能会因为某些类型错误感到困惑。这时候,不妨使用 // @ts-expect-error
来临时忽略这些错误。等你对 TypeScript 更加熟悉后,可以回过头来修复这些问题。
我们将在接下来的练习中更深入地使用 TypeScript,帮助你写出更加健壮的 React 代码。
在这个练习中,我们将为计算器组件添加类型安全,以确保左操作数、运算符和右操作数都具有正确的类型。这将帮助我们避免潜在的错误,比如传入一个字符串而不是数字。
首先,我们需要定义一个类型来表示计算器的属性(props)。这可以通过创建一个接口来完成:
interface CalculatorProps {
left: number; // 左操作数
operator: '+' | '-' | '*' | '/'; // 运算符
right: number; // 右操作数
}
接下来,我们将使用这个类型来更新计算器组件的 props:
const Calculator: React.FC<CalculatorProps> = ({ left, operator, right }) => {
let result: number;
switch (operator) {
case '+':
result = left + right;
break;
case '-':
result = left - right;
break;
case '*':
result = left * right;
break;
case '/':
result = right !== 0 ? left / right : 0; // 防止除以零
break;
default:
throw new Error(`Unknown operator: ${operator}`);
}
return (
<div>
<code>
{left} {operator} {right} = {result}
</code>
</div>
);
};
最后,我们可以在应用中使用这个组件,并确保传递正确的 props:
<Calculator left={1} operator="+" right={2} />
<Calculator left={5} operator="-" right={3} />
<Calculator left={4} operator="*" right={2} />
<Calculator left={10} operator="/" right={2} />
left
设置为一个字符串,TypeScript 将会抛出错误,提醒你这个 prop 的类型不匹配。operator
,我们使用了字符串字面量类型来限制允许的运算符,以便获得自动完成功能。通过这样的设置,我们的计算器组件现在就具备了类型安全,这不仅提高了代码的健壮性,也提高了开发过程中的便利性。您可以继续在应用中添加更多的计算器实例,看看 TypeScript 如何为您提供更好的开发体验。
在这个练习中,我们将通过添加类型安全来改进计算器组件的使用体验。这样,开发者在使用组件时可以获得更好的错误提示和自动补全功能。
CalculatorProps
类型首先,我们需要定义一个类型 CalculatorProps
,以确保传递给计算器组件的参数符合预期:
interface CalculatorProps {
left: number; // 左操作数
operator: '+' | '-' | '*' | '/'; // 运算符,可以限制为特定的字符串
right: number; // 右操作数
}
接下来,我们将使用这个类型更新计算器组件的 props:
const Calculator: React.FC<CalculatorProps> = ({ left, operator, right }) => {
let result: number;
switch (operator) {
case '+':
result = left + right;
break;
case '-':
result = left - right;
break;
case '*':
result = left * right;
break;
case '/':
result = right !== 0 ? left / right : 0; // 防止除以零
break;
default:
throw new Error(`Unknown operator: ${operator}`);
}
return (
<div>
<code>
{left} {operator} {right} = {result}
</code>
</div>
);
};
使用组件时,我们确保传递正确的类型。例如:
<Calculator left={1} operator="+" right={2} />
<Calculator left={5} operator="-" right={3} />
<Calculator left={4} operator="*" right={2} />
<Calculator left={10} operator="/" right={2} />
为了确保类型安全,我们可以故意传递错误的类型,比如将 left
设置为字符串,TypeScript 将会抛出错误,提醒你这个 prop 的类型不匹配:
<Calculator left={"one"} operator="+" right={2} /> // 会产生错误
通过这种方式,我们可以确保组件的 props 类型安全,同时在开发过程中获得更好的错误提示和自动补全功能。这样做不仅提高了代码的可维护性,也提升了开发者的体验。继续在应用中添加更多的计算器实例,测试 TypeScript 的类型检查功能,确保你熟悉它的用法。
要将 operator
的类型限制为特定的字符串(如“+”、“-”、“*”和“/”),我们可以在 CalculatorProps
接口中使用字符串字面量类型。这将使 TypeScript 在编译时检查 operator
的值是否符合这些特定的运算符。
CalculatorProps
类型首先,我们需要更新 CalculatorProps
接口,具体如下:
interface CalculatorProps {
left: number; // 左操作数
operator: '+' | '-' | '*' | '/'; // 限制运算符为特定的字符串
right: number; // 右操作数
}
在更新了类型定义后,我们的计算器组件会自动获得这些类型的安全性。代码如下:
const Calculator: React.FC<CalculatorProps> = ({ left, operator, right }) => {
let result: number;
switch (operator) {
case '+':
result = left + right;
break;
case '-':
result = left - right;
break;
case '*':
result = left * right;
break;
case '/':
result = right !== 0 ? left / right : 0; // 防止除以零
break;
default:
throw new Error(`Unknown operator: ${operator}`);
}
return (
<div>
<code>
{left} {operator} {right} = {result}
</code>
</div>
);
};
现在,当你尝试将不支持的运算符传递给 Calculator
组件时,TypeScript 会在编译时发出错误:
<Calculator left={1} operator="+" right={2} /> // 正确
<Calculator left={5} operator="-" right={3} /> // 正确
<Calculator left={4} operator="*" right={2} /> // 正确
<Calculator left={10} operator="/" right={2} /> // 正确
<Calculator left={10} operator="%" right={2} /> // 错误:类型“%”不可赋值给类型“'+' | '-' | '*' | '/'”。
通过将运算符类型限制为字符串字面量类型,开发者可以在编译时捕获潜在的错误,而不必在运行时检查。这提高了代码的安全性和可维护性。继续使用这些特性,在你的应用程序中进一步应用 TypeScript,确保在构建更复杂的功能时保持类型安全。
要限制运算符为特定字符串(如加法、减法、乘法和除法),并获得更好的类型安全和自动完成功能,可以使用 TypeScript 的字符串字面量类型。你已经成功实现了这个过程,并且得到了 TypeScript 的类型检查和代码编辑器的自动完成功能。这是 TypeScript 的强大之处,能够在开发过程中提供实时的反馈。
以下是你所做的关键步骤的总结:
定义 CalculatorProps
接口:
interface CalculatorProps {
left: number;
operator: '+' | '-' | '*' | '/'; // 使用字符串字面量类型限制运算符
right: number;
}
在组件中使用这些类型:
在 Calculator
组件中,你使用这些定义的类型来确保正确的参数传递。
const Calculator: React.FC<CalculatorProps> = ({ left, operator, right }) => {
// 计算逻辑...
};
错误处理:
通过 TypeScript 的类型检查,当你尝试传递不允许的运算符(如 **
或 ^
)时,编辑器将给出错误提示,而不必运行应用程序。
虽然当前实现已经很好地解决了问题,但你提到可以进一步优化。以下是一些潜在的改进方向:
使用枚举: 如果运算符数量较多或可能变化,考虑使用枚举来管理运算符。
enum Operator {
Add = '+',
Subtract = '-',
Multiply = '*',
Divide = '/',
}
interface CalculatorProps {
left: number;
operator: Operator; // 使用枚举类型
right: number;
}
自动化运算符提示: 结合 TypeScript 的类型提示功能,增强用户体验。例如,可以在函数内部提供详细的错误信息或提示,指导用户如何正确使用组件。
运行时检查: 尽管 TypeScript 提供了静态检查,但在组件运行时也可以加入检查,以确保运算符的有效性(例如,抛出错误或警告)。
通过这些方法,你不仅可以提高代码的可读性和可维护性,还能让使用组件的开发者获得更好的体验。继续探索 TypeScript 的特性,将其应用于你的 React 项目中,以构建更安全、更高效的应用程序!
为了使 TypeScript 的运算符更具可扩展性并避免手动添加每个运算符,你可以使用类似于之前所述的typeof
和keyof
关键字来创建派生类型。这样,我们就可以从现有的运算符对象中提取出所有的运算符,而不是手动维护一个字符串字面量类型。
以下是你可以遵循的步骤,以便在 TypeScript 中实现更灵活的运算符类型:
定义一个运算符对象: 首先,定义一个包含所有有效运算符的对象。这将允许你从这个对象中提取出运算符。
const operations = {
add: '+',
subtract: '-',
multiply: '*',
divide: '/',
} as const; // 使用 `as const` 来确保这是一个只读对象
提取运算符类型:
然后,使用typeof
和keyof
来提取运算符的类型。
type Operator = typeof operations[keyof typeof operations]; // 这将是 '+' | '-' | '*' | '/'
更新 CalculatorProps
接口:
修改 CalculatorProps
接口,使其使用新的运算符类型。
interface CalculatorProps {
left: number;
operator: Operator; // 使用派生的 Operator 类型
right: number;
}
以下是一个完整的示例代码,展示了如何实现上述步骤:
const operations = {
add: '+',
subtract: '-',
multiply: '*',
divide: '/',
} as const;
type Operator = typeof operations[keyof typeof operations]; // '+' | '-' | '*' | '/'
interface CalculatorProps {
left: number;
operator: Operator;
right: number;
}
const Calculator: React.FC<CalculatorProps> = ({ left, operator, right }) => {
let result: number;
switch (operator) {
case '+':
result = left + right;
break;
case '-':
result = left - right;
break;
case '*':
result = left * right;
break;
case '/':
result = left / right;
break;
default:
throw new Error(`Unsupported operator: ${operator}`);
}
return (
<div>
<code>
{left} {operator} {right} = {result}
</code>
</div>
);
};
通过这种方式,你可以方便地添加或删除运算符,而不必手动维护类型定义。当你更新运算符对象时,TypeScript 会自动反映这些更改,保持类型的一致性。这种方法在开发过程中可以减少错误并提高代码的可维护性。
很高兴你已经成功实现了运算符的类型派生!下面是对这个解决方案的进一步分析,帮助你理解这一过程的细节和优势。
你所使用的解决方案利用了 TypeScript 的类型系统,通过从一个包含有效运算符的对象中提取出运算符类型。这不仅简化了代码,还使得类型定义更加灵活和可扩展。
定义运算符对象:
你创建了一个对象 operations
,它包含了所有有效的运算符,并且使用 as const
确保它是一个只读的常量。这使得 TypeScript 能够推断出对象的字面量类型。
const operations = {
add: '+',
subtract: '-',
multiply: '*',
divide: '/',
// 可以在这里添加其他运算符
} as const;
提取运算符类型:
你通过使用 typeof
和 keyof
创建了一个运算符类型,这个类型自动从 operations
对象中派生而来。
type Operator = typeof operations[keyof typeof operations]; // 结果是 '+' | '-' | '*' | '/'
更新 CalculatorProps
接口:
最后,你更新了 CalculatorProps
接口,将 operator
的类型改为新的 Operator
类型。
interface CalculatorProps {
left: number;
operator: Operator; // 使用派生的 Operator 类型
right: number;
}
自动推断:通过这种方式,任何时候你更新 operations
对象,都能自动反映在 Operator
类型中,避免了手动同步的麻烦。
类型安全:TypeScript 会确保在使用组件时,只有有效的运算符可以被传递,这大大减少了运行时错误的风险。
开发体验:使用 TypeScript 提供的自动完成功能,使得编写代码时更加高效和愉快。
这种类型派生的方式非常有效,能够让你的代码更易于维护和扩展。继续保持这种方式,不断优化你的代码!如果有其他问题或需要进一步的帮助,请随时问我。
好的,我们来讨论如何为计算器组件设置默认值并使某些属性变为可选属性。在 TypeScript 中,我们可以通过定义接口来实现这一点,同时结合提供默认值的方式。下面是如何完成这一任务的步骤。
CalculatorProps
接口:使 left
、right
和 operator
属性变为可选。CalculatorProps
接口首先,我们需要更新 CalculatorProps
接口,以便将 left
、right
和 operator
属性标记为可选的。使用问号 ?
表示属性是可选的。
interface CalculatorProps {
left?: number; // 可选属性
operator?: Operator; // 可选属性
right?: number; // 可选属性
}
在你的计算器组件内部,你可以使用逻辑运算符来设置默认值。这样,如果传入的 props 中没有这些属性,组件就会使用默认值。
const Calculator: React.FC<CalculatorProps> = ({ left = 0, operator = '+', right = 0 }) => {
// 现在 left、operator 和 right 都有默认值
const result = operations[operator](left, right); // 计算结果
return (
<div>
<code>{`${left} ${operator} ${right}`}</code>
= <output>{result}</output>
</div>
);
};
下面是完整的示例代码,展示如何将所有这些组合在一起:
import React from 'react';
// 定义运算符
const operations = {
add: (a: number, b: number) => a + b,
subtract: (a: number, b: number) => a - b,
multiply: (a: number, b: number) => a * b,
divide: (a: number, b: number) => a / b,
} as const;
// 定义可选的运算符类型
type Operator = typeof operations[keyof typeof operations];
// 定义 CalculatorProps 接口
interface CalculatorProps {
left?: number;
operator?: Operator;
right?: number;
}
// Calculator 组件
const Calculator: React.FC<CalculatorProps> = ({ left = 0, operator = 'add', right = 0 }) => {
const result = operations[operator](left, right);
return (
<div>
<code>{`${left} ${operator} ${right}`}</code>
= <output>{result}</output>
</div>
);
};
// 使用 Calculator 组件
const App = () => (
<div>
<Calculator left={1} right={2} operator="add" />
<Calculator right={5} operator="multiply" />
<Calculator left={4} />
<Calculator />
</div>
);
export default App;
通过这些步骤,你已经成功地为计算器组件实现了可选属性和默认值。这不仅提高了组件的灵活性,也使得使用组件时的开发体验更加友好。如果用户不传入某些属性,组件会自动使用预设的默认值。这样,你可以确保组件始终能正常工作。继续加油!如果你还有其他问题,欢迎随时问我。
好的,我们来总结一下如何在 TypeScript 中为 React 组件设置可选属性和默认值。在这段代码中,我们的目标是为计算器组件的 left
、right
和 operator
属性提供默认值,并确保这些属性是可选的。
left
、right
和 operator
属性标记为可选属性。以下是一个简单的实现示例:
import React from 'react';
// 定义运算符类型
type Operator = 'add' | 'subtract' | 'multiply' | 'divide';
// 定义计算器属性接口
interface CalculatorProps {
left?: number; // 可选属性
operator?: Operator; // 可选属性
right?: number; // 可选属性
}
// 定义运算操作
const operations: Record<Operator, (a: number, b: number) => number> = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => a / b,
};
// Calculator 组件
const Calculator: React.FC<CalculatorProps> = ({ left = 0, operator = 'add', right = 0 }) => {
// 计算结果
const result = operations[operator](left, right);
return (
<div>
<code>{`${left} ${operator} ${right}`}</code>
= <output>{result}</output>
</div>
);
};
// 使用 Calculator 组件
const App = () => (
<div>
<Calculator left={1} right={2} operator="add" />
<Calculator right={5} operator="multiply" />
<Calculator left={4} />
<Calculator />
</div>
);
export default App;
接口定义:我们定义了 CalculatorProps
接口,其中 left
、right
和 operator
都是可选属性。
interface CalculatorProps {
left?: number;
operator?: Operator;
right?: number;
}
默认值设置:在组件定义中,我们为 left
、operator
和 right
属性设置了默认值。这意味着如果没有传入这些属性,组件会使用默认值:
const Calculator: React.FC<CalculatorProps> = ({ left = 0, operator = 'add', right = 0 }) => {
const result = operations[operator](left, right);
// ... 组件的返回部分
};
运算操作:使用一个记录类型 Record<Operator, (a: number, b: number) => number>
来定义运算操作,这样我们就可以安全地引用运算符,确保不会传入错误的运算符。
Calculator
组件而不传入某个属性时,比如不传入 left
,则会使用默认值 0
。通过这次的实现,你已经成功为计算器组件设置了可选属性和默认值。这不仅提高了组件的灵活性,也使得使用组件时的开发体验更加友好。继续加油,如果你有其他问题,随时可以问我!
在这一步中,我们将专注于优化 TypeScript 的类型定义,以便更好地处理数学运算,而不需要在每个操作中重复定义函数的类型。这将使我们能够轻松添加新的操作而不必担心类型声明。
我们希望创建一个操作类型,以便我们可以从中导出所需的参数类型和返回类型,而无需手动指定每个操作的详细类型。
Operation
,它是一个接受两个数字参数并返回数字的函数类型。下面是一个简化的实现示例:
import React from 'react';
// 定义运算符类型
type Operator = 'add' | 'subtract' | 'multiply' | 'divide';
// 定义操作类型
type Operation = (left: number, right: number) => number;
// 创建操作对象
const operations: Record<Operator, Operation> = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => a / b,
};
// 定义计算器属性接口
interface CalculatorProps {
left?: number;
operator?: Operator;
right?: number;
}
// 计算器组件
const Calculator: React.FC<CalculatorProps> = ({
left = 0,
operator = 'add',
right = 0,
}) => {
const result = operations[operator](left, right);
return (
<div>
<code>{`${left} ${operator} ${right}`}</code>
= <output>{result}</output>
</div>
);
};
// 使用 Calculator 组件
const App = () => (
<div>
<Calculator left={1} right={2} operator="add" />
<Calculator right={5} operator="multiply" />
<Calculator left={4} />
<Calculator />
</div>
);
export default App;
定义 Operation
类型:我们定义了一个 Operation
类型,表示接受两个 number
类型参数并返回 number
的函数。
type Operation = (left: number, right: number) => number;
创建 operations
对象:使用 Record<Operator, Operation>
来定义一个运算对象,允许我们将每个运算符与其对应的实现关联起来。
const operations: Record<Operator, Operation> = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => a / b,
};
计算器组件:在 Calculator
组件中,我们使用 operations
对象来执行所需的运算,而不需要为每个操作手动定义类型。
Operation
类型,我们减少了冗余的类型声明。operations
对象中添加新项即可,无需更改类型定义。通过以上步骤,我们成功地优化了 TypeScript 的类型定义,使得添加新操作变得更加容易。这样的结构不仅提高了代码的可读性和可维护性,也使得开发过程更加顺畅。如果你有其他问题或需要进一步的帮助,请随时告诉我!
在这个阶段,我们要进一步改进我们的 TypeScript 类型定义,以确保我们的计算器操作具备更好的类型安全和自动完成功能。我们需要处理的主要任务是定义一个 Operation
函数类型,并利用这个类型来确保我们能够限制操作符的可用性,同时又不会牺牲类型安全性。
Operation
函数类型,该类型接受两个数字参数并返回一个数字。以下是一个改进的实现示例:
import React from 'react';
// 定义运算符类型
type Operator = 'add' | 'subtract' | 'multiply' | 'divide';
// 定义运算类型
type Operation = (left: number, right: number) => number;
// 创建操作对象,使用 Record 来限定操作符
const operations: Record<Operator, Operation> = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => a / b,
};
// 定义计算器属性接口
interface CalculatorProps {
left?: number;
operator?: Operator;
right?: number;
}
// 计算器组件
const Calculator: React.FC<CalculatorProps> = ({
left = 0,
operator = 'add',
right = 0,
}) => {
const result = operations[operator](left, right);
return (
<div>
<code>{`${left} ${operator} ${right}`}</code>
= <output>{result}</output>
</div>
);
};
// 使用 Calculator 组件
const App = () => (
<div>
<Calculator left={1} right={2} operator="add" />
<Calculator right={5} operator="multiply" />
<Calculator left={4} />
<Calculator />
</div>
);
export default App;
定义操作符类型:我们定义了一个 Operator
类型,它只允许 'add'
, 'subtract'
, 'multiply'
, 和 'divide'
这四个字符串。
type Operator = 'add' | 'subtract' | 'multiply' | 'divide';
定义运算类型:定义了一个 Operation
类型,用于表示运算函数。
type Operation = (left: number, right: number) => number;
创建操作对象:使用 Record<Operator, Operation>
来确保 operations
对象中的每个运算符都与相应的操作函数匹配。
const operations: Record<Operator, Operation> = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => a / b,
};
定义计算器组件:在 Calculator
组件中,定义了 CalculatorProps
接口,以确保左侧和右侧参数为可选的数字,操作符为可选的 Operator
类型。
自动完成与类型检查:通过将操作符类型限制为一个小的字符串集,我们能够利用 TypeScript 的自动完成特性,确保用户在传递参数时仅能选择有效的操作符。
可选参数与默认值:我们为左侧和右侧数字以及操作符提供了默认值,使得使用计算器组件时更加灵活和友好。
通过以上步骤,我们成功实现了对计算器组件的类型安全检查和操作符限制,使得代码更加健壮。这样的结构不仅提高了代码的可读性和可维护性,也使得开发过程更加顺畅。如果你还有其他问题或者需要进一步的帮助,请随时告诉我!
在这个步骤中,我们将通过使用 TypeScript 的 satisfies
关键字来改进我们的 operations
对象的类型定义。这样做可以简化类型声明,同时保持类型安全和良好的开发体验。
satisfies
语法:我们将使 operations
对象满足更广泛的类型定义。operations
符合更广泛的类型,而不是强制性地将其定义为一个特定的类型。以下是如何实现的示例代码:
import React from 'react';
// 定义运算符类型
type Operator = 'add' | 'subtract' | 'multiply' | 'divide';
// 定义运算类型
type Operation = (left: number, right: number) => number;
// 创建操作对象
const operations = {
add: (a: number, b: number) => a + b,
subtract: (a: number, b: number) => a - b,
multiply: (a: number, b: number) => a * b,
divide: (a: number, b: number) => a / b,
} satisfies Record<Operator, Operation>; // 使用 satisfies 语法
// 定义计算器属性接口
interface CalculatorProps {
left?: number;
operator?: Operator;
right?: number;
}
// 计算器组件
const Calculator: React.FC<CalculatorProps> = ({
left = 0,
operator = 'add',
right = 0,
}) => {
const result = operations[operator](left, right);
return (
<div>
<code>{`${left} ${operator} ${right}`}</code>
= <output>{result}</output>
</div>
);
};
// 使用 Calculator 组件
const App = () => (
<div>
<Calculator left={1} right={2} operator="add" />
<Calculator right={5} operator="multiply" />
<Calculator left={4} />
<Calculator />
</div>
);
export default App;
运算符类型:我们定义了一个 Operator
类型,它限制了可用的运算符。
type Operator = 'add' | 'subtract' | 'multiply' | 'divide';
运算类型:定义了一个 Operation
类型,表示接收两个数字并返回一个数字的函数。
type Operation = (left: number, right: number) => number;
使用 satisfies
语法:在 operations
对象中,使用 satisfies
关键字来指定 operations
应符合 Record<Operator, Operation>
类型,而不强制为特定的类型。这使得我们可以更灵活地扩展 operations
对象而不会破坏类型检查。
const operations = {
add: (a: number, b: number) => a + b,
subtract: (a: number, b: number) => a - b,
multiply: (a: number, b: number) => a * b,
divide: (a: number, b: number) => a / b,
} satisfies Record<Operator, Operation>;
satisfies
关键字,我们能够减少冗余的类型定义,使代码更加简洁易读。operations
的类型检查,并确保我们只使用有效的操作符。通过这个步骤,我们不仅优化了类型定义,同时也提升了开发体验。你可以根据需要继续扩展 operations
对象,而不必担心手动维护类型。如果你有其他问题或需要进一步的帮助,请随时告诉我!
在这个步骤中,我们将优化我们对运算符类型的管理,使其更具灵活性,同时保持类型安全和良好的开发体验。我们将通过使用 TypeScript 的 satisfies
语法来实现这一点。
satisfies
语法:使 operations
对象能够满足更广泛的类型,而无需重复类型定义。operations
时仍能获得类型安全和 IDE 的自动补全功能。以下是更新后的代码示例:
import React from 'react';
// 定义运算类型
type Operation = (left: number, right: number) => number;
// 创建操作对象
const operations = {
add: (a: number, b: number) => a + b,
subtract: (a: number, b: number) => a - b,
multiply: (a: number, b: number) => a * b,
divide: (a: number, b: number) => a / b,
} satisfies Record<string, Operation>; // 使用 satisfies 语法
// 定义计算器属性接口
interface CalculatorProps {
left?: number;
operator?: keyof typeof operations; // 限制为 operations 对象的键
right?: number;
}
// 计算器组件
const Calculator: React.FC<CalculatorProps> = ({
left = 0,
operator = 'add',
right = 0,
}) => {
const result = operations[operator](left, right);
return (
<div>
<code>{`${left} ${operator} ${right}`}</code>
= <output>{result}</output>
</div>
);
};
// 使用 Calculator 组件
const App = () => (
<div>
<Calculator left={1} right={2} operator="add" />
<Calculator right={5} operator="multiply" />
<Calculator left={4} />
<Calculator />
</div>
);
export default App;
运算类型:定义了一个 Operation
类型,表示接收两个数字并返回一个数字的函数。
type Operation = (left: number, right: number) => number;
使用 satisfies
语法:在 operations
对象中,使用 satisfies
关键字来指定 operations
应符合 Record<string, Operation>
类型。这样,operations
可以是任意字符串映射到 Operation
类型的对象,同时保持对各个操作的类型检查和自动补全功能。
const operations = {
add: (a: number, b: number) => a + b,
subtract: (a: number, b: number) => a - b,
multiply: (a: number, b: number) => a * b,
divide: (a: number, b: number) => a / b,
} satisfies Record<string, Operation>;
限制运算符:通过 keyof typeof operations
,我们确保 operator
属性只能是 operations
对象中的有效键,从而增强了类型安全。
operator?: keyof typeof operations;
satisfies
,我们避免了单独定义运算符类型,从而减少了冗余代码。operations
中保持了类型检查和 IDE 的自动补全功能,确保开发过程顺利。通过这个步骤,我们不仅优化了运算符的类型管理,还提升了代码的可维护性和可扩展性。你可以根据需要继续扩展 operations
对象,而不必担心手动维护类型。如果你有其他问题或需要进一步的帮助,请随时告诉我!
这是一个很好的时刻来休息一下!给自己一些时间来补充水分,放松心情,帮助你更好地消化和巩固所学的知识。
在学习新内容时,特别是编程和技术方面,休息和自我照顾是非常重要的。就像你提到的,写下你所学到的东西,即使不再查看,写的过程本身也会帮助你记住这些信息。
当你准备好继续时,欢迎回来,我们将继续学习更多有趣的内容!如果你有任何问题或者需要进一步的帮助,请随时告诉我。
React 19 深入浅出, 构建生产级React应用程序 Learn React 19 with Epic React v2
1 React Fundamentals
001 Intro 002 Hello World in JS 003 Hello World in JS (1) 004 Generate the Root Node 005 Generate the Root Node (1) 006 Dad Joke Break 007 Intro to Raw React APIs 008 Create React Elements 009 Create React Elements (1) 010 Nesting Elements 011 Nesting Elements (1) 012 Deep Nesting Elements 013 Deep Nesting Elements (1) 014 Dad Joke Break Raw React APIs 015 Intro to Using JSX 016 Compiling JSX 017 Compiling JSX (1) 018 Interpolation 019 Interpolation (1) 020 Spread props 021 Spread props (1) 022 Nesting JSX 023 Nesting JSX (1) 024 Fragments 025 Fragments (1) 026 Dad Joke Break Using JSX 027 Intro to Custom Components 028 Simple Function 029 Simple Function (1) 030 Raw API 031 Raw API (1) 032 JSX Components 033 JSX Components (1) 034 Props 035 Props (1) 036 Dad Joke Break Custom Components 037 Intro to TypeScript 038 Props (2) 039 Props (3) 040 Narrow Types 041 Narrow Types (1) 042 Derive Types 043 Derive Types (1) 044 Default Props 045 Default Props (1) 046 Reduce Duplication 047 Reduce Duplication (1) 048 Satisfies 049 Satisfies (1) 050 Dad Joke Break TypeScript 051 Intro to Styling 052 Styling 053 Styling (1) 054 Custom Component 055 Custom Component (1) 056 Size Prop 057 Size Prop (1) 058 Dad Joke Break Styling 059 Intro to Forms 060 Form 061 Form (1) 062 Form Action 063 Form Action (1) 064 Input Types 065 Input Types (1) 066 Submission 067 Submission (1) 068 Form Actions 069 Form Actions (1) 070 Dad Joke Break Forms 071 Intro to Inputs 072 Checkbox 073 Checkbox (1) 074 Select 075 Select (1) 076 Radios 077 Radios (1) 078 Hidden Inputs 079 Hidden Inputs (1) 080 Default Value 081 Default Value (1) 082 Dad Joke Break Inputs 083 Intro to Error Boundaries 084 Composition 085 Composition (1) 086 Other Errors 087 Other Errors (1) 088 Reset 089 Reset (1) 090 Dad Joke Break Error Boundaries 091 Intro to Rendering Arrays 092 Key prop 093 Key prop (1) 094 Focus State 095 Focus State (1) 096 Key Reset 097 Key Reset (1) 098 Dad Joke Break Rendering Arrays 099 Outro to React Fundamentals
2 React Hooks 001 React Hooks Intro 002 Intro to Managing UI State 003 useState 004 useState (1) 005 Controlling Inputs 006 Controlling Inputs (1) 007 Derive State 008 Derive State (1) 009 Initialize State 010 Initialize State (1) 011 Init Callback 012 Init Callback (1) 013 Dad Joke Break Managing UI State 014 Intro to Side-Effects 015 useEffect 016 useEffect (1) 017 Effect Cleanup 018 Effect Cleanup (1) 019 Dad Joke Break Side-Effects 020 Intro to Lifting State 021 Lift State 022 Lift State (1) 023 Lift More State 024 Lift More State (1) 025 Colocate State 026 Colocate State (1) 027 Dad Joke Break Lifting State 028 Intro to DOM Side-Effects 029 Refs 030 Refs (1) 031 Dependencies 032 Dependencies (1) 033 Primitive Dependencies 034 Primitive Dependencies (1) 035 Dad Joke Break DOM Side-Effects 036 Intro to Unique IDs 037 useId 038 useId (1) 039 Dad Joke Break Unique IDs 040 Intro to Tic Tac Toe 041 setState callback 042 setState callback (1) 043 Preserve State in localStorage 044 Preserve State in localStorage (1) 045 Add Game History Feature 046 Add Game History Feature (1) 047 Dad Joke Break Tic Tac Toe 048 Outro to React Hooks
3 Advanced React APIs
Outro to Advanced React APIs
4 React Suspense
Outro to React Suspense
5 Advanced React Patterns
Outro to Advanced React Patterns
6 React Performance
Outro to React Performance
7 React Server Components
Outro to React Server Components
8 Bonus. Interviews With Experts
1 React基础 001 介绍
002 在JS中实现Hello World
003 在JS中实现Hello World (1)
004 生成根节点
005 生成根节点 (1)
006 爸爸笑话时间
007 原生React API简介
008 创建React元素
009 创建React元素 (1)
010 嵌套元素
011 嵌套元素 (1)
012 深度嵌套元素
013 深度嵌套元素 (1)
014 爸爸笑话时间 原生React API
015 使用JSX简介
016 编译JSX
017 编译JSX (1)
018 插值
019 插值 (1)
020 Spread props
021 Spread props (1)
022 嵌套JSX
023 嵌套JSX (1)
024 片段
025 片段 (1)
026 爸爸笑话时间 使用JSX
027 自定义组件简介
028 简单函数
029 简单函数 (1)
030 原生API
031 原生API (1)
032 JSX组件
033 JSX组件 (1)
034 Props
035 Props (1)
036 爸爸笑话时间 自定义组件
037 TypeScript简介
038 Props (2)
039 Props (3)
040 类型收窄
041 类型收窄 (1)
042 推导类型
043 推导类型 (1)
044 默认Props
045 默认Props (1)
046 减少重复
047 减少重复 (1)
048 Satisfies
049 Satisfies (1)
050 爸爸笑话时间 TypeScript
051 样式简介
052 样式
053 样式 (1)
054 自定义组件
055 自定义组件 (1)
056 尺寸Props
057 尺寸Props (1)
058 爸爸笑话时间 样式
059 表单简介
060 表单
061 表单 (1)
062 表单操作
063 表单操作 (1)
064 输入类型
065 输入类型 (1)
066 提交
067 提交 (1)
068 表单操作
069 表单操作 (1)
070 爸爸笑话时间 表单
071 输入简介
072 复选框
073 复选框 (1)
074 下拉选择
075 下拉选择 (1)
076 单选按钮
077 单选按钮 (1)
078 隐藏输入
079 隐藏输入 (1)
080 默认值
081 默认值 (1)
082 爸爸笑话时间 输入
083 错误边界简介
084 组合
085 组合 (1)
086 其他错误
087 其他错误 (1)
088 重置
089 重置 (1)
090 爸爸笑话时间 错误边界
091 数组渲染简介
092 Key prop
093 Key prop (1)
094 焦点状态
095 焦点状态 (1)
096 Key重置
097 Key重置 (1)
098 爸爸笑话时间 数组渲染
099 React基础结束
2 React钩子 001 React钩子简介
002 UI状态管理简介
003 useState
004 useState (1)
005 控制输入
006 控制输入 (1)
007 推导状态
008 推导状态 (1)
009 初始化状态
010 初始化状态 (1)
011 初始化回调
012 初始化回调 (1)
013 爸爸笑话时间 UI状态管理
014 副作用简介
015 useEffect
016 useEffect (1)
017 清理副作用
018 清理副作用 (1)
019 爸爸笑话时间 副作用
020 状态提升简介
021 提升状态
022 提升状态 (1)
023 提升更多状态
024 提升更多状态 (1)
025 状态合并
026 状态合并 (1)
027 爸爸笑话时间 状态提升
028 DOM副作用简介
029 Refs
030 Refs (1)
031 依赖项
032 依赖项 (1)
033 原始依赖项
034 原始依赖项 (1)
035 爸爸笑话时间 DOM副作用
036 唯一ID简介
037 useId
038 useId (1)
039 爸爸笑话时间 唯一ID
040 井字棋简介
041 setState回调
042 setState回调 (1)
043 在localStorage中保存状态
044 在localStorage中保存状态 (1)
045 添加游戏历史功能
046 添加游戏历史功能 (1)
047 爸爸笑话时间 井字棋
048 React钩子结束
3 高级React API
4 React Suspense
5 高级React模式
6 React性能优化
7 React服务器组件
组件简介
8 额外:专家访谈