{"author":{"address":"0x0c3743ac31156269ea0ea04bdb1864645017a92b","user":"https://learnblockchain.cn/people/19204"},"content":{"body":"# 项目构建\r\n\r\n创建react项目\r\n\r\n```\r\nnpm create vite\r\n```\r\n\r\n之后选择框架 react，之后选择自己熟悉的语言，之后的项目构建使用的是TypeScript\r\n\r\n```\r\ncd react-app //进入项目目录\r\nnpm i \t\t//安装第三方依赖\r\nnpm run dev //启动本地服务器\r\n```\r\n\r\n编译项目\r\n\r\n```\r\nnpm run build\r\nyarn build\r\n```\r\n\r\n启动开发服务器\r\n\r\n```\r\nnpm run dev\r\n```\r\n\r\n# React目录结构\r\n\r\n\r\n![image-20250308220730414.png](https://img.learnblockchain.cn/attachments/2025/03/Um1oqH5r67cd13dd59931.png)\r\n\r\n## src\r\n\r\n这是项目的“心脏”，你写的代码和资源都在这里。src 是“source”（源代码）的缩写。\r\n\r\n- **assets**：放静态资源，比如图片、字体这些东西\r\n\r\n- **components**：常用命名习惯。存放一个个组件。组件相当于一个功能，比如一个按钮、一个消息框\r\n\r\n- **vite-env.d.ts**：vite-env.d.ts 是一个专门用来声明 Vite 项目中环境类型定义的文件。通常，Vite 项目会自动生成这个文件，并默认包含这行指令。\r\n\r\n  这个文件初始的代码\r\n\r\n  ```react\r\n  /// \u003creference types=\"vite/client\" /\u003e\r\n  ```\r\n\r\n  这行代码的主要目的是让 TypeScript 能够识别和使用 Vite 在**客户端（client-side）**提供的类型定义。vite/client 是 Vite 内置的一个类型声明文件，里面包含了 Vite 在客户端运行时提供的全局变量、函数和模块的类型信息。\r\n\r\n##  public\r\n\r\n这个文件夹放了一些不会被代码加工的文件，比如图片、图标或者 HTML 模板，这些是“静态”的东西。\r\n\r\n# React文件结构\r\n\r\n## index.html\r\n\r\n```react\r\n\u003c!DOCTYPE html\u003e //声明这是一个 HTML5 文档\r\n\u003chtml lang=\"en\"\u003e //lang=\"en\" 表示页面语言为英语。\r\n  \u003chead\u003e //标签包含页面的元数据和外部资源\r\n    \u003cmeta charset=\"UTF-8\" /\u003e\r\n    \u003clink rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" /\u003e //设置浏览器标签页的图标（favicon），这里使用 Vite 的 SVG 图标\r\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /\u003e //控制移动设备上的视口，使页面宽度适配设备屏幕，默认缩放比例为 1。\r\n    \u003ctitle\u003eVite + React + TS\u003c/title\u003e //定义浏览器标签页的标题，这里是 “Vite + React + TS”\r\n  \u003c/head\u003e\r\n  \u003cbody\u003e //标签包含页面的可见内容和脚本加载逻辑\r\n    \u003cdiv id=\"root\"\u003e\u003c/div\u003e 一个空的 \u003cdiv\u003e 元素，ID 为 root，是 React 应用的挂载点\r\n    \u003cscript type=\"module\" src=\"/src/main.tsx\"\u003e\u003c/script\u003e //引入 React 应用的入口文件 /src/main.tsx，并以 ES 模块方式加载。\r\n  \u003c/body\u003e\r\n\u003c/html\u003e\r\n\r\n```\r\n\r\n1. **提供 HTML 结构**：为 React 应用提供一个基本的 HTML 框架，作为应用的“外壳”。\r\n2. **设置元数据**：通过 head 中的标签，确保页面在不同设备和浏览器中正确显示。\r\n3. **挂载 React 应用**：通过 div id=\"root\"  提供一个 DOM 节点，供 React 渲染组件。\r\n4. **加载脚本**：通过 script 标签引入应用的入口文件，启动 React 的运行逻辑。换句话讲，就是所有组件的集合文件，所有单个组件的逻辑融合在一起，被加载出来。\r\n\r\n## main.tsx\r\n\r\n```react\r\nimport React from 'react' // 导入了 React 库，它提供了创建组件、使用 JSX 语法以及管理虚拟 DOM 的能力\r\nimport ReactDOM from 'react-dom/client' // ReactDOM 库的 client 模块，ReactDOM 是 React 和浏览器 DOM（文档对象模型）之间的桥梁。createRoot 方法来自这个模块，用于创建 React 应用的根节点，从这里开始渲染组件。\r\nimport App from './App' //App 是应用的根组件，包含了整个应用的结构和逻辑，其他子组件会嵌套在其中。这个是我们后续完善单个组件融合之后的子集合文件。\r\nimport 'bootstrap/dist/css/bootstrap.css' //Bootstrap 的 CSS 文件\r\n\r\nReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(\r\n  \u003cReact.StrictMode\u003e\r\n    \u003cApp /\u003e\r\n  \u003c/React.StrictMode\u003e,\r\n)\r\n\r\n```\r\n\r\nBootstrap 是一个流行的 CSS 框架，提供了预定义的样式类（比如按钮、网格、表单等），导入后可以在项目中直接使用这些样式来美化界面。\r\n\r\n安装命令\r\n\r\n```\r\nnpm i bootstrap@5.2.3\r\n```\r\n\r\n我们这里将原来的 index.css 文件删除了。组件使用预定义好的css样式。\r\n\r\n## App.tsx\r\n\r\n这是一个根组件文件，Button 和 Alert 是两个子组件。\r\n\r\n```react\r\nimport Button from \"./components/Button\";\r\nimport Alert from \"./components/Alert\";\r\nimport { useState } from \"react\"; //React 提供的一个 Hook，专门用于在函数组件中管理状态（state）\r\n\r\nfunction App() {\r\n  const [alert,setalert] = useState(false);\r\n  return (\r\n    \u003cdiv\u003e\r\n      {alert \u0026\u0026 \u003cAlert onClose={() =\u003e setalert(false)}\u003eMy alert\u003c/Alert\u003e}\r\n      \u003cButton color = \"secondary\" onClick={() =\u003e setalert(true)}\u003eMY Button\u003c/Button\u003e\r\n    \u003c/div\u003e\r\n  );\r\n}\r\n\r\nexport default App;\r\n\r\n```\r\n\r\n- **const [alert,setalert] = useState(false);**\r\n\r\n  它返回一个数组，数组中有两个元素，第一个元素是状态变量（这里是 alert），表示当前的状态值。第二个元素是更新状态的函数（这里是 setAlert），用于修改状态变量的值。\r\n\r\n  这里定义了一个状态变量 alert，它的初始值被设置为 false，setAlert 是一个函数，通过调用它可以更新 alert 的值。就比如 `onClick={() =\u003e setalert(true)}` ，我们向 setalert 函数传入true参数来更改 alert 的值\r\n\r\n\r\n### ListGroup.tsx\r\n\r\n这一个列表功能的组件。我们这里只定义了组件的逻辑，而参数是通过接口 Props 传入的。\r\n\r\n```react\r\nimport { Fragment, useState } from \"react\";\r\n\r\ninterface Props {\r\n    items : string[];\r\n    heading: string;\r\n    onselect:(item: string) =\u003e void\r\n};\r\n\r\n\r\nfunction ListGroup({items,heading,onselect}:Props) {\r\n  \r\n  //Hook\r\n  const [selectedIndex, setSelectedIndex] = useState(-1);\r\n\r\n\r\n  const handleSelectItem = (item : string) =\u003e {\r\n    console.log(item);\r\n  }\r\n \r\n  const getMessage = () =\u003e {\r\n    return items.length === 0 ? \u003cp\u003eNo item found\u003c/p\u003e : null;\r\n  };\r\n\r\n  return (\r\n    \u003c\u003e\r\n      \u003ch1\u003e{heading}\u003c/h1\u003e\r\n      {items.length === 0 \u0026\u0026 \u003cp\u003eNo item found\u003c/p\u003e}\r\n      \u003cul className=\"list-group\"\u003e\r\n        {items.map((item, index) =\u003e (\r\n          \u003cli\r\n            className={\r\n              selectedIndex === index\r\n                ? \"list-group-item active\"\r\n                : \"list-group-item\"\r\n            }\r\n            key={item}\r\n            onClick={() =\u003e {\r\n                setSelectedIndex(index);\r\n                onselect(item)\r\n            }}\r\n          \u003e\r\n            {item}\r\n          \u003c/li\u003e\r\n        ))}\r\n      \u003c/ul\u003e\r\n    \u003c/\u003e\r\n  );\r\n}\r\n\r\nexport default ListGroup;\r\n\r\n```\r\n\r\n- **className命名**\r\n\r\n  在 React 中，要给 HTML 元素添加 CSS 类，必须使用 className，而不是 HTML 中的 class。这是因为 class 是 JavaScript 的保留关键字，在 React 的 JSX 语法中会引起冲突， class 不是有效的属性。\r\n\r\n- **{items.length === 0 \u0026\u0026 \u003cp\u003eNo item found\u003c/p\u003e}**\r\n\r\n  - 在 React 的 JSX 语法中，花括号 {} 是用来嵌入 JavaScript 表达式的。当你需要在 JSX 中运行 JavaScript 代码并将结果插入到标记中时，必须用 {} 包裹它。这样 JSX 解析器就知道这里是 JavaScript，而不是普通的文本。\r\n  - 这里使用了逻辑与进行运算\r\n\r\n- Fragment 的用法\r\n\r\n  ```react\r\n  Fragment 允许你将多个元素分组，而不在 DOM 中添加额外节点。一个组件return只能返回一个节点，节点也就是指元素：如 \u003cdiv\u003e、\u003ch1\u003e、\u003cp\u003e ，或者是文本节点\u003cul\u003e等。相比使用\u003cdiv\u003e来包裹返回一个对象，使用 \u003cFragment\u003e\u003c/Fragment\u003e 或者简写为  \u003c\u003e\u003c/\u003e。使用 \u003cdiv\u003e 作为根元素时，会在最终的 DOM 树中多出一个 \u003cdiv\u003e 节点，而使用 Fragment（可以用 \u003c\u003e...\u003c/\u003e 表示）时，不会生成额外的节点。最终只会返回下面的结构\r\n      \r\n  \u003ch1\u003e标题\u003c/h1\u003e\r\n  \u003cp\u003e内容\u003c/p\u003e\r\n  ```\r\n- map方法\r\n\r\n  map 方法的作用是将 items 数组中的每个字符串转换为一个带有动态样式和点击事件的 li 元素，并将这些元素组合成一个列表渲染到页面。自动遍历数组里面的元素。\r\n\r\n- key 属性的作用\r\n\r\n  在 React 中，当你用 map 方法渲染一个列表时，比如显示一组数据，React 需要知道每个列表项的“身份”。key 就是一个唯一的标识符，帮助 React 高效地追踪和管理这些列表项。\r\n\r\n  假设待办事项是这样的\r\n\r\n  ```\r\n  const items = [\"买牛奶\", \"写作业\", \"打扫房间\"];\r\n  ```\r\n\r\n  用户把“打扫房间”移到第一位，列表变成：\r\n\r\n  ```\r\n  const items = [  { id: 3, text: \"打扫房间\" },  { id: 1, text: \"买牛奶\" },  { id: 2, text: \"写作业\" } ];\r\n  ```\r\n\r\n  React 看到 key=3 跑到第一位，key=1 和 key=2 跟着调整位置。它知道这些项只是换了顺序，不会重新渲染内容，而是直接移动现有的 DOM 节点，结果是：\r\n\r\n  ```\r\n  - 打扫房间 (key=3) - 买牛奶 (key=1) - 写作业 (key=2)\r\n  ```\r\n\r\n  如果不使用key\r\n\r\n  React 会认为：\r\n\r\n  - 第一项从“买牛奶”变成了“打扫房间”\r\n  - 第二项从“写作业”变成了“买牛奶”\r\n  - 第三项从“打扫房间”变成了“写作业”\r\n\r\n  结果是 React 可能会重新更新所有项的内容，而不是简单地调整顺序，这样效率很低。\r\n 避免问题：如果列表项有状态（比如输入框），key 确保状态跟正确的项绑定，不会错乱\r\n\r\n### Alert.tsx\r\n\r\n```react\r\nimport React, { ReactNode } from \"react\";\r\n\r\ninterface Props {\r\n  children: ReactNode;//表示 Alert 组件可以接受子元素。\r\n  onClose: () =\u003e void;//表示 Alert 组件接受一个名为 onClose 的函数\r\n}\r\n\r\nconst Alert = ({ children, onClose }: Props) =\u003e {\r\n  return (\r\n    \u003cdiv className=\"alert alert-primary d-flex justify-content-between align-items-center\"\u003e\r\n      \u003cspan\u003e{children}\u003c/span\u003e\r\n      \u003cbutton\r\n        type=\"button\"\r\n        className=\"btn-close\"\r\n        data-bs-dismiss=\"alert\"\r\n        data-bs-target=\"#my-alert\"\r\n        aria-label=\"Close\"\r\n        onClick={onClose} // 将 onClick 移到按钮上\r\n      \u003e\u003c/button\u003e\r\n    \u003c/div\u003e\r\n  );\r\n};\r\n\r\nexport default Alert;\r\n```\r\n\r\n这里规定了参数的格式。接口 Props 定义了 Alert 组件所接受的 props 的类型\r\n\r\n- **children: ReactNode**\r\n\r\n  当使用 TypeScript 开发 React 组件时，为了确保代码的类型安全，必须为组件的 props 定义类型。如果不给 children 指定类型，TypeScript 无法推断它是什么，可能会导致类型错误，或者 IDE 无法提供准确的代码提示。因此，显式声明 children 的类型是必要的\r\n\r\n  ReactNode 是 React 提供的一个类型别名（type alias），它描述了 React 组件可以接受的所有可能的 children 类型。具体来说，ReactNode 包括：\r\n\r\n  - **ReactElement**：如 \u003cdiv /\u003e 或 \u003cspan /\u003e\r\n  - **string**：纯文本内容\r\n  - **number**：数字\r\n  - **boolean**：布尔值（通常不会直接渲染）\r\n  - **null** 或 **undefined**：空值\r\n  - **ReactFragment**：如 \u003c\u003e\u003c/\u003e\r\n  - **ReactPortal**：React 传送门\r\n  - **ReactNode[]**：多个子元素的数组\r\n\r\n- **className=\"btn-close\"**\r\n\r\n  这里的样式是Bootstrap提供的。\r\n\r\n### Button.tsx\r\n\r\n```react\r\nimport React from \"react\";\r\n\r\ninterface Props {\r\n  children: string;\r\n  onClick: () =\u003e void;\r\n  color: string;\r\n}\r\n\r\nconst Button = ({ children, onClick, color }: Props) =\u003e {\r\n  return (\r\n    \u003cbutton className={\"btn btn-\" + color} onClick={onClick}\u003e\r\n      {children}\r\n    \u003c/button\u003e\r\n  );\r\n};\r\n\r\nexport default Button;\r\n\r\n```","title":"React快速了解"},"history":null,"timestamp":1741493880,"version":1}