Dark Dwarf Blog background

React 基础

React 基础

1. React 环境搭建

a.a. 使用 Vite 创建项目

# 'my-react-app' 可以替换为你想要的任何项目名
npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
npm run dev

b.b. 项目结构

Vite 创建的项目结构如下:

  • public/: 存放静态资源,如图片、图标等。此文件夹中的内容在构建时会被直接复制到输出目录。
  • src/: 存放应用的源代码。
    • main.jsx: 应用的入口文件。它负责将 React 应用挂载到 HTML 页面上。
    • App.jsx: 一个示例的根组件。
    • index.css: 全局 CSS 文件。
  • index.html: 应用的 HTML 入口。与传统项目不同,Vite 将 index.html 作为应用的中心,脚本通过 <script type="module"> 标签引入。
  • package.json: 项目的配置文件,包含依赖项和脚本命令。

c.c. 入口文件解析 (main.jsx)

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

// 1. 使用 ReactDOM.createRoot 创建一个 React 根
//    它指定了 React 应用在哪个 DOM 元素内进行渲染
const root = ReactDOM.createRoot(document.getElementById('root'));

// 2. 使用 root.render() 方法渲染你的应用
//    <App /> 就是我们要渲染的 React 组件
//    <React.StrictMode> 是一个辅助组件,用于检查应用中潜在的问题
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);

2. React 组件

a.a. 基本概念

React 的核心思想就是组件化。它将复杂的 UI 拆分成独立的、可复用的部分,我们称之为“组件” (Components)。每个组件都可以有自己的逻辑和外观。

在现代 React 中,我们主要使用函数式组件。它就是一个返回 JSX 的 JavaScript 函数。

// src/components/Greeting.jsx

// 这是一个简单的函数式组件
function Greeting() {
  // 它返回一个 JSX 元素
  return <h1>Hello, World!</h1>;
}

// 导出组件,以便在其他地方使用
export default Greeting;

组件有如下的关键规则:

  1. 组件名称必须大写: Greeting 而不是 greeting。React 通过首字母大小写来区分是 React 组件还是普通的 HTML 标签。
  2. 组件必须返回一个元素: 组件必须返回单个 JSX 元素。如果想返回多个元素,需要用一个父元素(如 <div>)或 Fragment (<>...</>) 包裹起来。

b.b. 组合组件:importexport

组件的强大之处在于它们的可组合性。我们可以通过 importexport 机制来组织和复用组件。

导出 (Export)

  • 默认导出 (export default): 每个文件只能有一个默认导出。这是最常见的导出组件的方式。
    // Greeting.jsx
    function Greeting() { /* ... */ }
    export default Greeting;
  • 命名导出 (export): 每个文件可以有多个命名导出。导入时需要使用花括号 {} 并且名称必须匹配。
    // Helpers.js
    export function helperOne() { /* ... */ }
    export const PI = 3.14;

导入 (Import)

  • 导入默认导出:
    // App.jsx
    import MyGreetingComponent from './components/Greeting.jsx';
  • 导入命名导出:
    // App.jsx
    import { helperOne, PI } from './Helpers.js';

现在,我们可以将我们创建的 Greeting 组件用到 App.jsx 中:

// src/App.jsx
import Greeting from './components/Greeting.jsx';

function App() {
  return (
    <div>
      <h1>My Application</h1>
      <Greeting /> {/*像 HTML 标签一样使用组件*/}
    </div>
  );
}

export default App;

3. JSX

a.a. JSX 基本概念

JSX 是 JavaScript 的语法扩展,它允许你在 JavaScript 代码中编写类似 HTML 的结构。虽然不是必须的,但它让编写 React 组件变得极其直观和简洁。

本质上,JSX 只是 React.createElement(component, props, ...children) 函数的语法糖。我们写的每一个 JSX 标签最终都会被 Babel 这样的编译器转换为一个普通的 JavaScript 对象。

// 这段 JSX...
const element = <h1 className="greeting">Hello, world!</h1>;

// ...会被 Babel 这样的编译器转换为:
const element = React.createElement('h1', {className: 'greeting'}, 'Hello, world!');

使用 JSX 的主要好处是它允许我们将渲染逻辑和 UI 结构自然地放在同一个地方——组件中,这使得代码更易于理解和维护。

b.b. JSX 的核心规则

直接将 HTML 复制到 JSX 中通常是行不通的,因为 JSX 有自己的一套规则。

  1. 必须返回单个根元素:一个组件返回的 JSX 必须被一个单独的父标签包裹。如果不想在 DOM 中添加额外的 <div>,可以使用Fragment (<></>)。
// 正确:使用 Fragment 包裹
function App() {
  return (
    <>
      <h1>Example h1</h1>
      <h2>Example h2</h2>
    </>
  );
}

// 错误:返回了两个顶级元素
/*
function App() {
  return (
    <h1>Example h1</h1>
    <h2>Example h2</h2>
  );
}
*/
  1. 必须闭合所有标签:在 HTML 中,像 <input><li> 这样的标签可以不闭合。但在 JSX 中,所有标签都必须显式闭合。对于自闭合标签,在末尾加上 / 即可。
// 正确
function Form() {
  return (
    <>
      <input type="text" />
      <ul>
        <li>Item 1</li>
      </ul>
    </>
  );
}

// 错误
/*
function Form() {
  return (
    <>
      <input type="text">
      <li>Item 1
    </>
  );
}
*/
  1. 使用驼峰命名法 (camelCase) 定义属性:因为 JSX 最终会转换为 JavaScript 对象,而对象的键不能包含破折号或 JavaScript 的保留字(如 class)。因此,大多数 HTML 属性在 JSX 中需要使用驼峰命名法。
  • class 变为 className
  • stroke-width 变为 strokeWidth
  • onclick 变为 onClick
// 正确
function MyComponent() {
  return <div className="container" onClick={() => console.log('Clicked!')} />;
}

// 错误
/*
function MyComponent() {
  return <div class="container" onclick="alert('hello')"></div>;
}
*/

c.c. 在 JSX 中使用 JavaScript

JSX 的真正威力在于它能够与 JavaScript 无缝集成。我们可以使用花括号 {} 将 JavaScript 逻辑嵌入到你的标记中。

  1. 引用变量:我们可以将任何 JavaScript 变量(字符串、数字、数组等)直接渲染到 UI 中。
const userName = "Ada Lovelace";

function Profile() {
  return <h1>Welcome, {userName}!</h1>;
}
  1. 在属性中使用:花括号也可以用于动态设置属性。
const avatarUrl = 'https://i.imgur.com/7vQD0fPs.jpg';

function Avatar() {
  return <img src={avatarUrl} alt="User Avatar" />;
}
  1. 调用函数和使用表达式:我们可以在花括号内放置任何有效的 JavaScript 表达式,比如函数调用、三元运算符或数组方法。
function ShoppingList() {
  const items = ['Apple', 'Banana', 'Cherry'];
  return (
    <ul>
      {items.map(item => <li key={item}>{item}</li>)}
    </ul>
  );
}
  1. “双花括号” 与内联样式:在 JSX 中为元素添加内联样式时,有时会看到 style={{}} 这样的语法。这并不是什么特殊的语法,而是“在花括号内传入一个 JavaScript 对象”。
  • 第一个花括号 {}: 表示我们要嵌入一个 JavaScript 表达式。
  • 第二个花括号 {}: 表示我们传入的表达式本身是一个样式对象。

样式对象的属性也必须使用驼峰命名法。

function Panel() {
  return (
    <div style={{ 
      backgroundColor: 'black', 
      color: 'pink',
      fontSize: '16px' 
    }}>
      This is a styled panel.
    </div>
  );
}