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();
});
相关推荐
To_OC21 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic1 天前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy1 天前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志1 天前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble1 天前
c端系统,其实更像一个信息展示平台
前端
李姆斯1 天前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜1 天前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司1 天前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
kaiking_g1 天前
vue项目中,静态导入 vs 动态导入 详解
前端·javascript·vue.js
江华森1 天前
Web 开发基础:HTTP 与 REST
前端·网络协议·http