React基础、进阶(学习用)

一、React 基础三要素:JSX、组件、Props

1. JSX

使用方法

  • 用类似 HTML 的语法写界面,在 {} 中写 JavaScript 表达式。
  • 属性用驼峰命名:classNamehtmlFor
  • 必须有根标签,可用空标签 <></>
js 复制代码
const name = "Alice";
const el = <h1 className="title">Hello, {name}</h1>;

底层原理

JSX 会被 Babel 编译为 React.createElement(type, props, ...children) 调用,返回一个 虚拟 DOM 对象(描述 UI 的普通 JS 对象)

js 复制代码
// 编译后
React.createElement('h1', { className: 'title' }, 'Hello, ', name);

2. 函数组件与 Props

使用方法

  • 组件名首字母大写。
  • 通过参数接收 props(只读对象)。
js 复制代码
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
// 使用:<Welcome name="Bob" />

原理

组件是一个函数,返回虚拟 DOM。React 调用该函数,得到虚拟 DOM 并渲染。


二、State:组件自己的记忆

1. useState

使用方法

js 复制代码
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      {count}
    </button>
  );
}

原理

  • useState 在组件 fiber 节点上维护一个 Hook 链表,存储状态。
  • setCount 会创建一个更新对象,触发调度更新。
  • 批量更新 :React 18 中,同一事件处理函数内的多次 setState 会被合并,只重渲染一次。
  • 用函数形式更新可避免闭包陷阱:setCount(prev => prev + 1)

三、事件处理

使用方法

js 复制代码
function App() {
  function handleClick(e) {
    e.preventDefault();
    console.log('clicked');
  }
  return <button onClick={handleClick}>Click</button>;
}

原理

React 使用合成事件(SyntheticEvent) ,包裹原生事件,统一跨浏览器行为。事件挂载在根节点,通过事件委托触发。


四、条件渲染与列表渲染

使用方法

  • 条件:{isShow && <Comp />}{isShow ? <A /> : <B />}
  • 列表:arr.map(item => <li key={item.id}>{item.name}</li>)

key 的作用和原理

  • key 帮助 React 在 Diff 时识别节点是否可复用。
  • 使用 index 做 key 在顺序变化时会导致状态混乱(Diff 失败)。
  • 底层:Diff 算法通过对比新旧虚拟 DOM 的 key 决定移动、复用或重建。

五、表单:受控与非受控

使用方法

受控组件

js 复制代码
const [value, setValue] = useState('');
<input value={value} onChange={e => setValue(e.target.value)} />

非受控组件

js 复制代码
const inputRef = useRef();
<input ref={inputRef} />
// 取值: inputRef.current.value

原理

  • 受控:React state 作为"唯一数据源",输入值完全由 state 控制。
  • 非受控:DOM 自身维护状态,通过 ref 读取。

六、Hooks 全面详解

1. useEffect ------ 处理副作用

使用方法

js 复制代码
useEffect(() => {
  const timer = setInterval(() => {...}, 1000);
  return () => clearInterval(timer); // 清理函数
}, [deps]);
  • 依赖数组:[] 只挂载执行;[count] count 变化执行;不传则每次渲染都执行。

原理

  • React 在渲染完成后(绘制屏幕后)异步执行 effect。
  • 清理函数在下一次 effect 执行前或组件卸载时运行。
  • React 18 严格模式会故意挂载两次,帮助发现副作用问题。

2. useLayoutEffect ------ 同步执行

使用方法

js 复制代码
useLayoutEffect(() => {
  // 在浏览器绘制之前执行
}, []);

原理

  • 执行时机:DOM 更新后,浏览器绘制前,同步执行
  • 通常用于读取布局信息并同步重绘,避免闪烁。

3. useContext ------ 跨组件数据

使用方法

js 复制代码
const ThemeCtx = createContext('light');
function Child() {
  const theme = useContext(ThemeCtx);
  return <div>{theme}</div>;
}
function App() {
  return (
    <ThemeCtx.Provider value="dark">
      <Child />
    </ThemeCtx.Provider>
  );
}

原理

  • Provider 将值记录在 fiber 节点上,子组件通过 useContext 直接读取,并自动订阅更新。
  • 性能注意 :Provider value 变化会导致所有消费组件重渲染,可用 memo 或拆分 Context 优化。

4. useReducer ------ 复杂状态

使用方法

js 复制代码
const reducer = (state, action) => {
  switch (action.type) {
    case 'inc': return { count: state.count + 1 };
    default: return state;
  }
};
const [state, dispatch] = useReducer(reducer, { count: 0 });
// 调用:dispatch({ type: 'inc' });

原理

  • 和 Redux 思想一致:派发 action,reducer 返回新状态。
  • dispatch 是稳定引用,不会随渲染变化。
  • 底层 useState 实际上就是用 useReducer 实现的。

5. useMemo / useCallback ------ 缓存

使用方法

js 复制代码
const memoizedValue = useMemo(() => heavyCompute(a, b), [a, b]);
const memoizedFn = useCallback(() => { doSth(a, b); }, [a, b]);

原理

  • 依赖不变时返回缓存的值/函数,避免子组件无意义重渲染(需配合 React.memo)。
  • useCallbackuseMemo 的语法糖:useMemo(() => fn, deps)

6. useRef ------ 穿透渲染周期

使用方法

  • 访问 DOM:<input ref={ref} />
  • 保存可变值:ref.current = value,改变不会触发重渲染。

原理

  • ref 对象在组件的整个生命周期保持不变。
  • 底层 fiber 节点上保存 ref 对象。

7. useImperativeHandle ------ 限制暴露的 ref

使用方法

js 复制代码
const Child = forwardRef((props, ref) => {
  useImperativeHandle(ref, () => ({ focus: () => inputRef.current.focus() }));
  return <input ref={inputRef} />;
});
// 父组件:childRef.current.focus()

8. 自定义 Hook ------ 逻辑复用

示例:useFetch

js 复制代码
function useFetch(url) {
  const [data, setData] = useState(null);
  useEffect(() => {
    let ignore = false;
    fetch(url).then(res => res.json()).then(d => {
      if (!ignore) setData(d);
    });
    return () => { ignore = true; };
  }, [url]);
  return data;
}

规则

  • 命名以 use 开头。
  • 内部可以使用其他 Hooks。

七、虚拟 DOM 与 Diff 算法

原理

  • 虚拟 DOM:用 JS 对象描述真实 DOM(类型、属性、子节点)。

  • Diff:对比新旧两棵虚拟 DOM 树,找出最小更新。

  • 算法前提

    • 同层比较(O(n) 复杂度)。
    • 不同类型节点直接替换。
    • key 标识列表节点。

流程

  • 根节点类型不同 → 销毁重建。
  • 同类型元素 → 复用 DOM,仅更新属性。
  • 同类型组件 → 实例不变,更新 props。
  • 子节点:使用 key 进行双端比较(Vue 类似),React 也用类似策略。

八、Fiber 架构(React 16+)

为什么需要 Fiber?

  • React 15 的 Stack Reconciler 递归不可中断,长任务会卡顿。
  • Fiber 将工作拆分为小单元,可暂停、继续、放弃。

Fiber 节点

  • 每个元素/组件都有对应的 fiber 节点,形成链表(child, sibling, return)。
  • 双缓冲current 树(当前屏幕)和 workInProgress 树(在内存中构建)。

渲染过程

  • 协调阶段(可中断) :构建 workInProgress 树,Diff 对比,标记副作用(增/删/改)。
  • 提交阶段(不可中断) :同步将变更应用到真实 DOM。

时间切片

  • 通过 requestIdleCallback(或 Scheduler 模拟)在空闲时执行工作单元。
  • 每个单元执行后检查是否还有时间,无则让出主线程。

九、React 18 并发特性

1. 自动批处理

  • 在事件、setTimeout、Promise 等所有更新中都默认批处理,减少渲染次数。

2. useTransition / startTransition

js 复制代码
const [isPending, startTransition] = useTransition();
startTransition(() => {
  setSearchQuery(input);
});
  • 标记非紧急更新,可被更高优先级的更新中断(如用户输入)。

3. useDeferredValue

js 复制代码
const deferredValue = useDeferredValue(value);
  • 延迟渲染不紧急的值,让出主线程。

4. Suspense 增强

  • 可配合数据获取,组件在数据准备好前显示 fallback。

十、状态管理

1. Redux Toolkit

使用方法

js 复制代码
import { createSlice, configureStore } from '@reduxjs/toolkit';
const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    incremented: state => { state.value += 1; }
  }
});
const store = configureStore({ reducer: counterSlice.reducer });
// 组件中使用 useSelector、useDispatch

原理

  • 单一 store,状态不可变,通过 dispatch action → reducer 生成新状态。
  • 内部用 useSyncExternalStore 订阅 store 变化(React 18)。

2. Zustand

js 复制代码
import { create } from 'zustand';
const useStore = create(set => ({
  count: 0,
  inc: () => set(state => ({ count: state.count + 1 }))
}));
// 组件中:const count = useStore(s => s.count);
  • 无 Provider,直接选择状态,按需渲染。

十一、React Router v6

使用方法

js 复制代码
import { BrowserRouter, Routes, Route, Link, useParams } from 'react-router-dom';
function App() {
  return (
    <BrowserRouter>
      <Link to="/user/123">User</Link>
      <Routes>
        <Route path="/user/:id" element={<User />} />
      </Routes>
    </BrowserRouter>
  );
}
function User() {
  const { id } = useParams();
  return <div>{id}</div>;
}
  • 编程式导航:useNavigate()

十二、Next.js 与 SSR

基本概念

  • Server Component(默认):在服务器运行,无客户端 JS,可直接访问数据库。
  • Client Component'use client' 标记,可使用 hooks 和交互。

数据获取

js 复制代码
// app/page.js (Server Component)
export default async function Page() {
  const data = await fetch('https://...', { next: { revalidate: 60 } });
  return <div>{data.title}</div>;
}

水合(Hydration)

  • 服务端返回 HTML,客户端加载 JS 后,React 将事件绑定到已有 DOM,不重新生成。

十三、React 19 新特性

  • use() :读取 Promise 或 Context,可中断渲染。
  • Actions<form action={serverAction}> 直接处理表单。
  • useOptimistic:乐观更新。
  • ref 作为普通 prop ,无需 forwardRef
  • 简化 Context 写法<Context> 即 Provider。

十四、性能优化

  1. React.memo:浅比较 props,无变化跳过渲染。
  2. useMemo / useCallback:缓存值/函数。
  3. 虚拟列表react-window 只渲染可视区。
  4. 代码分割React.lazy(() => import('./Comp')) + <Suspense>
  5. 避免内联对象/函数作为 props。
  6. 使用 Profiler 和 React DevTools 分析

十五、测试

  • Vitest + React Testing Library
js 复制代码
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

test('button click', async () => {
  render(<Counter />);
  await userEvent.click(screen.getByText('+1'));
  expect(screen.getByText('1')).toBeInTheDocument();
});
相关推荐
风骏时光牛马1 小时前
HTML十大经典实战代码案例合集
前端
weedsfly1 小时前
前端必知必会:从 IIFE 到 ESM,模块化到底在解决什么?
前端·javascript
spmcor1 小时前
为什么页面越用越卡?——React组件内存泄漏的排查与修复
react.js
笨鸟飞不快2 小时前
从单个服务到集群:一次完整的性能排查复盘
java·前端
禅思院2 小时前
Vite vs Webpack 深度对比:从启动原理到生产构建,一篇就够了
前端·架构·前端框架
IT_陈寒2 小时前
Vue的响应式真把我坑惨了,原来问题出在这
前端·人工智能·后端
朦胧之12 小时前
AI 编程-老项目改造篇
java·前端·后端
swipe15 小时前
从 0 到 1 实现大文件上传:分片、秒传、断点续传、暂停、重试与服务端合并
前端·javascript·面试
爱勇宝15 小时前
我做了一个只用来搜歌词的小 App
android·前端·后端