在React的世界里,我们使用React Elements、ReactDOM和React Components这三个核心概念来构建我们的应用。在本篇文章中,我们将详细解析这三个概念,并通过实例代码来展示它们的使用方法。
HTML页面准备
首先,我们打开已经创建好的项目,在index.html
文件中可以看到<div id="root"></div>
标签,之后我们的代码将会挂载到该id为root的DOM标签上。
React Elements 与 ReactDOM
React Element :React Element是React应用的最小构建块。它描述了在屏幕上看到的内容。在代码中,React Element通常由React.createElement()
方法创建。
ReactDOM:ReactDOM提供了一种将React Element渲染到DOM的方法。
React.createElement()
方法接收三个参数:
- 参数1:元素类型(如"h1")
- 参数2:元素属性(如null)
- 参数3:元素的子元素(如"辰火流光的React学习笔记")
tsx
// main.tsx文件
import React from "react";
import ReactDOM from 'react-dom/client'
// 创建了一个表示`<h1>`标签的React Element
const tagH1 = React.createElement("h1", null, "辰火流光的React学习笔记");
// 将上面创建的`tagH1`元素渲染到id为"root"的DOM元素中
ReactDOM.createRoot(document.getElementById('root')!).render(tagH1)
在这个例子中,ReactDOM.createRoot()
方法创建了一个根React元素,然后render()
方法将tagH1
元素渲染到<div id="root"></div>
元素中。
在浏览器中效果
在TypeScript类型声明文件中,我们可以看到createRoot
函数返回的是一个Root类型的对象。这个Root接口中包含了两个方法,其中render
方法接收的参数类型为React.ReactNode
。
React.ReactNode
是React中定义的一种类型,它包括了我们可以在React组件中渲染的所有类型的值,包括字符串、数字、React元素、数组、Fragment等。这意味着我们可以将这些类型的值作为参数传递给render
方法。
例如,我们可以创建两个React元素,然后将它们放在一个数组中,作为参数传递给render
方法:
tsx
import React from "react";
import ReactDOM from 'react-dom/client'
const tagH1 = React.createElement("h1", null, "辰火流光");
const tagH2= React.createElement("h2", null, "React学习笔记");
ReactDOM.createRoot(document.getElementById('root')!).render([tagH1,tagH2])
在浏览器中的效果
然而,当我们这样做时,浏览器的控制台会出现一个警告:Each child in a list should have a unique "key" prop.
。这是因为当我们在React中渲染数组时,每个元素都需要一个唯一的key
属性。
key
属性是React用来追踪哪些元素被修改、添加或删除的一种方式。在这个例子中,我们可以通过添加key
属性来解决这个警告:
tsx
import React from "react";
import ReactDOM from 'react-dom/client'
const tagH1 = React.createElement("h1", {key: "h1"}, "辰火流光");
const tagH2= React.createElement("h2", {key: "h2"}, "React学习笔记");
ReactDOM.createRoot(document.getElementById('root')!).render([tagH1,tagH2])
现在假设我们要创建以下html:
html
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
</ul>
我们可以使用React.createElement
函数来逐一创建每个li
元素,然后将它们作为子元素传递给ul
元素:
tsx
import React from "react";
import ReactDOM from 'react-dom/client'
const tagUL=React.createElement(
"ul",
null,
React.createElement("li", {key:1}, "Item 1"),
React.createElement("li", {key:2}, "Item 2"),
React.createElement("li", {key:3}, "Item 3"),
React.createElement("li", {key:4}, "Item 4"),
React.createElement("li", {key:5}, "Item 5"),
React.createElement("li", {key:6}, "Item 6")
);
ReactDOM.createRoot(document.getElementById('root')!).render(tagUL)
然而,这种方式存在一定的局限性。如果列表项的数量或内容需要根据数据动态变化,那么手动创建每个元素就会变得非常繁琐。这时,我们可以利用JavaScript的数组映射方法(map
)来动态生成元素。
我们可以先定义一个包含所有列表项内容的数组,然后使用map
方法将每个内容映射为一个li
元素:
tsx
import React from "react";
import ReactDOM from 'react-dom/client'
const items = [
"Item 1",
"Item 2",
"Item 3",
"Item 4",
"Item 5",
"Item 6",
];
const tagUL=React.createElement(
"ul",
null,
items.map((item,index) => React.createElement("li", {key:index}, item))
);
ReactDOM.createRoot(document.getElementById('root')!).render(tagUL)
在浏览器中的效果
这样,无论列表项的数量或内容如何变化,我们都可以轻松地生成对应的列表。
需要注意的是,当我们在React中渲染数组时,每个元素都需要一个唯一的key
属性。这是因为key
属性可以帮助React识别哪些元素被修改、添加或删除,从而提高渲染性能。
React Components
React Component是一种函数或类,它接收输入(称为"props")并返回一个React Element。例如,以下代码定义了一个名为"MyComponent"的函数组件:
tsx
import React from "react";
import ReactDOM from 'react-dom/client'
function MyComponent() {
return React.createElement(
"ul",
{ className: "myClass" },
React.createElement("li", null, "Item 1"),
React.createElement("li", null, "Item 2"),
React.createElement("li", null, "Item 3"),
React.createElement("li", null, "Item 4"),
React.createElement("li", null, "Item 5"),
React.createElement("li", null, "Item 6"),
);
}
ReactDOM.createRoot(document.getElementById('root')!).render(React.createElement(MyComponent, null, null))
或
tsx
import React from "react";
import ReactDOM from 'react-dom/client'
const items = [
"Item 1",
"Item 2",
"Item 3",
"Item 4",
"Item 5",
"Item 6",
];
function MyComponent() {
return React.createElement(
"ul",
{ className: "myClass" },
items.map((item,index) => React.createElement("li", {key:index}, item))
);
}
ReactDOM.createRoot(document.getElementById('root')!).render(React.createElement(MyComponent, null, null))
在这个例子中,MyComponent
函数返回了一个表示<ul>
标签的React Element,这个<ul>
标签包含了六个<li>
标签的子元素。
在浏览器中的效果
组件传参:
React Component可以接收props作为输入。例如,以下代码将一个包含六个item的数组作为props传递给MyComponent
组件:
tsx
import React from "react";
import ReactDOM from 'react-dom/client'
const items = [
"Item 1",
"Item 2",
"Item 3",
"Item 4",
"Item 5",
"Item 6",
];
function MyComponent(props) {
return React.createElement(
"ul",
{ className: "myClass" },
props.myList.map((item,index) => React.createElement("li", {key:index}, item))
);
}
ReactDOM.createRoot(document.getElementById('root')!).render(React.createElement(MyComponent, {myList:items}, null))
在这个例子中,MyComponent
函数接收一个名为props
的参数,这个参数是一个对象,包含了一个名为myList
的属性。然后,MyComponent
函数使用props.myList
来创建<li>
标签的子元素。
在React的学习过程中,我们首先接触到的可能是createElement
函数。通过它,我们可以深入理解React如何工作,了解React是如何将JavaScript和DOM结合起来的。然而,作为React开发人员,我们并不总是直接使用createElement
函数。毕竟,编写复杂、几乎不可读的JavaScript语法树并不是我们的目标。我们希望的是能够更高效、更直观地创建React元素。这就需要引入JSX。
JSX,或JavaScript XML,是一种JavaScript的扩展语法。它结合了JavaScript的逻辑处理能力和XML的标签语法,让我们可以直接在JavaScript代码中使用类似HTML的标签语法来定义React元素。这使得我们的代码更加直观、易读,也更接近我们熟悉的HTML编写方式。
尽管JSX看起来非常像HTML,但它们并不完全相同。JSX实际上是JavaScript的一部分,它只是提供了一种更直观、更易于编写的方式来创建React元素。这意味着,当我们在JSX中编写标签语法时,背后实际上还是通过React的createElement
函数来创建元素的。
在下一章节中,我们讨论如何使用 JSX 构建 React 应用程序。