Skip to content

React 基础

为初学者和中级开发者提供的 React 基础知识全面指南。

React 简介

React 是一个用于构建用户界面的 JavaScript 库,特别适用于单页应用程序。它用于处理 Web 和移动应用中的视图层。React 允许您为应用程序中的每个状态设计简单的视图,当数据发生变化时,它会高效地更新和渲染正确的组件。

React 的主要特点

  • 基于组件的架构:构建封装的组件,每个组件管理自己的状态
  • 声明式 UI:为应用程序中的每个状态设计简单的视图
  • 虚拟 DOM:当数据变化时高效地更新和渲染组件
  • JSX:JavaScript 语法扩展,允许在 JavaScript 中使用类似 HTML 的代码
  • 单向数据流:数据从父组件流向子组件

React 入门

前提条件

在开始学习 React 之前,您应该具备:

  • HTML、CSS 和 JavaScript 的基础知识
  • 理解 ES6 特性(箭头函数、类、解构等)
  • 系统上已安装 Node.js 和 npm

设置 React 项目

使用 Create React App(推荐给初学者)

bash
# 全局安装 Create React App
npm install -g create-react-app

# 创建新的 React 项目
npx create-react-app my-react-app

# 导航到项目目录
cd my-react-app

# 启动开发服务器
npm start

使用 Vite(更快的替代方案)

bash
# 使用 Vite 创建新的 React 项目
npm create vite@latest my-react-app -- --template react

# 导航到项目目录
cd my-react-app

# 安装依赖
npm install

# 启动开发服务器
npm run dev

核心概念

1. 组件

组件是 React 应用程序的构建块。它们是可重用的代码片段,返回描述屏幕上应该显示内容的 React 元素。

函数组件

jsx
function Welcome(props) {
  return <h1>你好,{props.name}</h1>;
}

// 使用箭头函数语法
const Welcome = (props) => {
  return <h1>你好,{props.name}</h1>;
};

类组件

jsx
import React, { Component } from 'react';

class Welcome extends Component {
  render() {
    return <h1>你好,{this.props.name}</h1>;
  }
}

2. JSX

JSX 是 JavaScript 的语法扩展,看起来类似于 HTML。它使 React 代码更具可读性和表现力。

jsx
const element = <h1>你好,世界!</h1>;

// 带表达式的 JSX
const name = '张三';
const element = <h1>你好,{name}!</h1>;

// 带属性的 JSX
const element = <img src={user.avatarUrl} alt={user.name} />;

// 带子元素的 JSX
const element = (
  <div>
    <h1>你好!</h1>
    <p>很高兴在这里见到你。</p>
  </div>
);

3. Props

Props(属性的缩写)是组件的只读输入。它们允许您将数据从父组件传递到子组件。

jsx
// 父组件
function App() {
  return <Welcome name="小明" />;
}

// 子组件
function Welcome(props) {
  return <h1>你好,{props.name}</h1>;
}

4. State

State 是存储可能随时间变化的组件数据的 JavaScript 对象。当状态变化时,组件会重新渲染。

jsx
import React, { useState } from 'react';

function Counter() {
  // 声明一个名为"count"的状态变量,初始值为0
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>你点击了 {count} 次</p>
      <button onClick={() => setCount(count + 1)}>
        点击我
      </button>
    </div>
  );
}

5. 生命周期和副作用

在函数组件中,useEffect 钩子让您执行副作用(如数据获取、订阅或 DOM 操作)。

jsx
import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // 类似于 componentDidMount 和 componentDidUpdate
  useEffect(() => {
    // 使用浏览器 API 更新文档标题
    document.title = `你点击了 ${count} 次`;
    
    // 可选的清理函数(类似于 componentWillUnmount)
    return () => {
      document.title = 'React App';
    };
  }, [count]); // 仅当 count 变化时重新运行副作用

  return (
    <div>
      <p>你点击了 {count} 次</p>
      <button onClick={() => setCount(count + 1)}>
        点击我
      </button>
    </div>
  );
}

处理事件

React 事件使用驼峰命名法,并作为函数而非字符串传递。

jsx
function ActionButton() {
  function handleClick(e) {
    e.preventDefault();
    console.log('按钮被点击了');
  }

  return (
    <button onClick={handleClick}>
      点击我
    </button>
  );
}

条件渲染

您可以使用 JavaScript 运算符如 if 或条件(三元)运算符来创建表示当前状态的元素。

jsx
function UserGreeting(props) {
  return <h1>欢迎回来!</h1>;
}

function GuestGreeting(props) {
  return <h1>请注册。</h1>;
}

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  
  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}

// 使用三元运算符
function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  
  return (
    <>
      {isLoggedIn ? <UserGreeting /> : <GuestGreeting />}
    </>
  );
}

列表和键

React 使用键来识别列表中哪些项目已更改、添加或删除。

jsx
function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>
      {number}
    </li>
  );
  
  return (
    <ul>{listItems}</ul>
  );
}

表单

在 React 中,表单元素自然保持一些内部状态。要拥有受控组件,您需要使用 React 状态管理表单数据。

jsx
import React, { useState } from 'react';

function NameForm() {
  const [value, setValue] = useState('');

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  const handleSubmit = (event) => {
    alert('提交的名字是:' + value);
    event.preventDefault();
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        名字:
        <input type="text" value={value} onChange={handleChange} />
      </label>
      <input type="submit" value="提交" />
    </form>
  );
}

最佳实践

组件组织

  • 保持组件小巧,专注于单一职责
  • 使用一致的文件结构(例如,每个文件一个组件)
  • 将相关组件分组到文件夹中

性能优化

  • 对经常使用相同 props 渲染的函数组件使用 React.memo
  • 使用 useCallback 钩子来记忆函数
  • 使用 useMemo 钩子来记忆昂贵的计算
  • 使用 React DevTools Profiler 识别性能瓶颈
jsx
import React, { memo, useCallback, useMemo } from 'react';

const ExpensiveComponent = memo(function ExpensiveComponent({ data, onItemClick }) {
  // 此组件仅在 data 或 onItemClick 变化时重新渲染
  return (
    <div>
      {data.map(item => (
        <div key={item.id} onClick={() => onItemClick(item.id)}>
          {item.name}
        </div>
      ))}
    </div>
  );
});

function ParentComponent() {
  const [items, setItems] = useState([/* 一些数据 */]);
  
  // useCallback 记忆函数
  const handleItemClick = useCallback((id) => {
    console.log('项目被点击:', id);
  }, []);
  
  // useMemo 记忆计算值
  const sortedItems = useMemo(() => {
    return [...items].sort((a, b) => a.name.localeCompare(b.name));
  }, [items]);
  
  return (
    <ExpensiveComponent 
      data={sortedItems} 
      onItemClick={handleItemClick} 
    />
  );
}

代码风格和约定

  • 使用带钩子的函数组件而非类组件
  • 对 props 和 state 使用解构
  • 使用展开运算符复制对象和数组
  • 遵循 Create React App 推荐的 ESLint 规则

常见模式

用于状态管理的 Context API

用于在不使用 prop 钻取的情况下跨多个组件共享状态:

jsx
import React, { createContext, useContext, useState } from 'react';

// 创建上下文
const ThemeContext = createContext();

// 提供者组件
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  
  const toggleTheme = () => {
    setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
  };
  
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// 消费者组件
function ThemedButton() {
  const { theme, toggleTheme } = useContext(ThemeContext);
  
  return (
    <button 
      onClick={toggleTheme}
      style={{ 
        background: theme === 'light' ? '#fff' : '#333',
        color: theme === 'light' ? '#333' : '#fff'
      }}
    >
      切换主题
    </button>
  );
}

// 应用
function App() {
  return (
    <ThemeProvider>
      <div>
        <h1>主题示例</h1>
        <ThemedButton />
      </div>
    </ThemeProvider>
  );
}

自定义钩子

将可重用逻辑提取到自定义钩子中:

jsx
import { useState, useEffect } from 'react';

// 用于获取数据的自定义钩子
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        const response = await fetch(url);
        
        if (!response.ok) {
          throw new Error(`HTTP 错误!状态:${response.status}`);
        }
        
        const result = await response.json();
        setData(result);
        setError(null);
      } catch (error) {
        setError(error.message);
        setData(null);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
}

// 使用自定义钩子
function UserProfile({ userId }) {
  const { data, loading, error } = useFetch(`https://api.example.com/users/${userId}`);

  if (loading) return <div>加载中...</div>;
  if (error) return <div>错误:{error}</div>;
  
  return (
    <div>
      <h1>{data.name}</h1>
      <p>邮箱:{data.email}</p>
    </div>
  );
}

常见问题解答

state 和 props 有什么区别?

  • Props 是从父组件传递给组件的,是只读的
  • State 是在组件内部管理的,可以使用 setState 或 useState 更新

什么时候应该使用类组件与函数组件?

在现代 React(16.8+)中,带钩子的函数组件适用于大多数用例。类组件仍然受支持,但主要用于遗留代码或特定用例。

如何在 React 中处理表单?

使用受控组件,其中表单数据由 React 状态处理,或者对于复杂表单,使用 Formik 或 React Hook Form 等库。

如何为 React 组件添加样式?

几种方法:

  • 使用 className 的常规 CSS
  • 使用 style 属性的内联样式
  • CSS 模块
  • CSS-in-JS 库(styled-components, emotion)
  • 实用优先的 CSS 框架(Tailwind CSS)

如何优化 React 中的性能?

  • 对函数组件使用 React.memo
  • 使用 useCallback 和 useMemo 钩子
  • 对长列表实现虚拟化
  • 使用 React.lazy 和 Suspense 进行代码分割
  • 避免不必要的重新渲染

相关资源


本文档将持续更新,如有问题请通过 GitHub Issues 反馈。

vitepress开发指南