React 源码学习 Day 1:React 包入口与核心 API
日期:2026-03-17 目标:理解 React 整体架构,掌握核心 API 适合:初级/中级前端,资深面试打基础
📌 本章总结
1. React 核心包架构
| 包名 | 职责 |
|---|---|
react | 核心 API(React.xxx) |
react-dom | DOM 渲染器 |
react-reconciler | 协调器(Fiber、Diff) |
scheduler | 任务调度器 |
2. React 包导出内容
入口:packages/react/index.js
↓
packages/react/src/ReactClient.js
↓
暴露以下 API:
2.1 组件定义
export { Component, PureComponent }
2.2 元素创建
export { createElement, cloneElement, isValidElement }
2.3 Children 处理
export { Children } // .map, .forEach, .count, .toArray, .only
2.4 Ref 和 Context
export { createRef, createContext, forwardRef }
2.5 Hooks(30+ 个)
useState, useEffect, useContext, useCallback,
useMemo, useRef, useReducer, useLayoutEffect,
useInsertionEffect, useImperativeHandle, useDebugValue,
useTransition, useDeferredValue, useId,
useSyncExternalStore, useOptimistic, useActionState, ...
2.6 高级特性
export { memo, lazy, Suspense, Profiler, Fragment, StrictMode }
2.7 并发特性(React 18+)
export { useTransition, startTransition, useDeferredValue }
👴 老大爷能听懂版
React = 大厨房
| 厨房里的 | React 中的 |
|---|---|
| 原料(米面肉菜) | createElement - 原材料 |
| 灶台(炒菜的地方) | Component - 怎么做 |
| 菜单(写着菜名) | Fragment - 一个占位符 |
| 招牌(今日特惠) | Profiler - 性能监控 |
| 仓库(放半成品) | createRef - 存东西 |
| 传菜口(厨房到前厅) | Context - 传递数据 |
| 冰箱 | useState - 存状态 |
| 定时器 | useEffect - 到点干活 |
| 传送带 | useContext - 跨组件传数据 |
1. Component / PureComponent — 组件的"模具"
场景: 你要生产一批手机壳
| 说明 | |
|---|---|
Component | 普通模具,生产慢,每次都要重新检查 |
PureComponent | 高级模具,有"记忆",如果原料没变就不重新生产 |
怎么用:
// 普通模具
class App extends Component { }
// 高级模具
class App extends PureComponent { }
2. createElement — 原料采购单
场景: 做饭前要写采购清单
// 采购清单:我要一个红色按钮
createElement('button', { style: { color: 'red' } }, '点我')
就是告诉厨房:"我要做个红色按钮,上面写着'点我'"。
3. cloneElement — 复印
场景: 菜单打印机
你有一张菜单,想复印一张改个名字:
const newButton = cloneElement(oldButton, { text: '新按钮' })
4. Children — 一群小孩
场景: 幼儿园老师管孩子
// 有很多小孩(children)
<Container>
<Kid1 />
<Kid2 />
<Kid3 />
</Container>
Children 就是帮老师管理这些小孩的工具:
Children.map— 挨个点名Children.forEach— 每人发个糖Children.only— 只能有一个小孩
5. createRef — 贴标签
场景: 图书馆给每本书贴标签
const myInput = createRef();
<input ref={myInput} />
贴上标签后,你可以直接找到这本书(DOM 节点)。
6. createContext + useContext — 传送带
场景: 火锅店的自助调料台
const ThemeContext = createContext('dark');
function App() {
return (
<ThemeContext.Provider value="light">
<Child />
</ThemeContext.Provider>
);
}
function Child() {
const theme = useContext(ThemeContext); // "light"
}
调料台(Context)放那儿,所有桌(组件)都能直接去蘸料(用 useContext)。
7. forwardRef — 转交标签
场景: 快递中转站
有时候标签不能直接给,需要中转一下:
const MyInput = forwardRef((props, ref) => {
return <input ref={ref} {...props} />;
});
8. Hooks 们 — 厨房工具箱
| 工具 | 作用 | 比喻 |
|---|---|---|
useState | 存状态 | 冰箱,存食材 |
useEffect | 副作用 | 定时器,到点干活 |
useContext | 拿数据 | 传送带 |
useRef | 存东西不渲染 | 贴标签 |
useReducer | 复杂状态管理 | 配菜师 |
useMemo | 记计算结果 | 备忘录 |
useCallback | 记函数 | 记事本 |
useLayoutEffect | 同步副作用 | 急单,先干 |
9. memo — 高级模具
场景: 精密仪器生产
如果参数没变,就不重新生产:
const Baby = memo(function Baby({ name }) {
return <div>{name}</div>;
});
10. lazy + Suspense — 点外卖
场景: 下馆子不想等
const Menu = lazy(() => import('./Menu'));
function App() {
return (
<Suspense fallback={<Loading />}>
<Menu />
</Suspense>
);
}
lazy= 点外卖Suspense= 等餐时的座位(loading)
11. Fragment — 空座位
场景: 拼桌
function App() {
return (
<>
<div>A</div>
<div>B</div>
</>
);
}
<> 就是 Fragment,什么也不添加,只是把两个 div 放一起。
12. Profiler — 监控摄像头
场景: 流水线的监控
<Profiler id="App" onRender={callback}>
<App />
</Profiler>
看看哪个环节最慢。
13. StrictMode — 安全检查
场景: 安检门
<StrictMode>
<App />
</StrictMode>
帮你检查有没有用过期 API、是否有隐患。
14. 并发三兄弟 — 加速器
| 加速器 | 作用 |
|---|---|
useTransition | 我先干这个,其他的缓缓 |
startTransition | 同上,函数版 |
useDeferredValue | 这个不急,渲染慢点没事 |
💻 开发者视角
核心文件对应关系
packages/react/src/
├── ReactClient.js ← 统一出口,所有 API 从这里导出
├── ReactBaseClasses.js ← Component, PureComponent 实现
├── ReactCreateRef.js ← createRef 实现
├── ReactChildren.js ← Children 处理
├── ReactContext.js ← createContext 实现
├── ReactHooks.js ← 所有 Hooks 入口(分发到 reconciler)
├── ReactMemo.js ← memo 实现
├── ReactForwardRef.js ← forwardRef 实现
├── ReactLazy.js ← lazy 实现
└── jsx/
└── ReactJSXElement.js ← createElement, JSX
关键代码解读
ReactClient.js 的核心逻辑:
// 1. 导��各模块
import {Component, PureComponent} from './ReactBaseClasses';
import {createRef} from './ReactCreateRef';
import {createElement, cloneElement, isValidElement} from './jsx/ReactJSXElement';
import {useState, useEffect, ...} from './ReactHooks';
// ...
// 2. 统一导出
export {
Children,
Component,
PureComponent,
createElement,
createRef,
useState,
useEffect,
// ... 30+ API
};
结论:React 包只是一个"API 封装层",真正的实现都在 react-reconciler 里!
各 API 源码解读
1. Component / PureComponent
// packages/react/src/ReactBaseClasses.js
function Component(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
Component.prototype.isReactComponent = {};
Component.prototype.setState = function(partialState, callback) {
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
区别:
Component:每次都执行shouldComponentUpdate(默认返回 true)PureComponent:自动执行浅比较shouldComponentUpdate
// PureComponent 的核心
pureComponentPrototype.isPureReactComponent = true;
// 内部会做 shallowEqual(prevProps, nextProps)
2. createElement
// packages/react/src/jsx/ReactJSXElement.js
export function createElement(type, config, children) {
// 1. 处理 key 和 ref
// 2. 构造 props
// 3. 返回 ReactElement 对象
return ReactElement(type, key, ref, props, ...);
}
返回值:
{
$$typeof: Symbol(react.element),
type: 'button' / Component, // 类型
key: null, // 唯一标识
ref: null, // 引用
props: { ... }, // 属性 + children
}
3. Children
// packages/react/src/ReactChildren.js
export function forEachChildren(children, callback, context) {
// 遍历 children
}
export function mapChildren(children, func, context) {
// 映射 children
}
注意: Children.map 会处理数组、字符串、数字等各种情况。
4. createRef
// packages/react/src/ReactCreateRef.js
export function createRef(): RefObject {
const refObject = {
current: null,
};
if (__DEV__) {
Object.seal(refObject);
}
return refObject;
}
特点: 返回一个带 current 属性的对象,修改 current 不会触发重渲染。
5. createContext
// packages/react/src/ReactContext.js
export function createContext(defaultValue) {
const context = {
$$typeof: REACT_CONTEXT_TYPE,
_currentValue: defaultValue,
Provider: null,
// ...
};
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};
return context;
}
6. memo
// packages/react/src/ReactMemo.js
export function memo(type, compare) {
return {
$$typeof: REACT_MEMO_TYPE,
type,
compare: compare || shallowEqual, // 默认浅比较
};
}
7. lazy
// packages/react/src/ReactLazy.js
export function lazy(promise) {
return {
$$typeof: REACT_LAZY_TYPE,
_payload: promise,
_init: readLazyValue,
};
}
📝 API 总结表格
| API | 作用 | 栗子 |
|---|---|---|
Component | Class 组件基类 | class A extends Component |
PureComponent | 浅比较的 Class 组件 | class A extends PureComponent |
createElement | 创建 React 元素 | createElement('div', null, 'hi') |
cloneElement | 复制并修改元素 | cloneElement(el, {key: 'new'}) |
Children | 处理 children 工具 | Children.map(children, fn) |
createRef | 创建 ref 对象 | const ref = createRef() |
createContext | 创建 Context | const ctx = createContext(默认值) |
useContext | 消费 Context | useContext(ctx) |
forwardRef | 转发 ref | forwardRef((props, ref) => ...) |
memo | 缓存组件 | memo(Component) |
lazy | 懒加载 | lazy(() => import('./A')) |
Suspense | 加载占位 | <Suspense fallback={<Loading/>}> |
Fragment | 分组不渲染 | <><a/><b/></> |
Profiler | 性能监控 | <Profiler onRender={fn}> |
StrictMode | 严格模式 | <StrictMode>A</StrictMode> |
🎯 面试必考点
Q1: React 项目有哪些核心包?各自职责是什么?
答:
react:提供核心 API(Component、createElement、useState 等)react-dom:负责把 React 元素渲染成真实 DOMreact-reconciler:协调器,负责 Fiber 树的构建和 Diff 算法scheduler:任务调度器,负责优先级调度
Q2: 函数组件和 Class 组件的区别?
答:
- Class 组件继承
Component,有this.state和生命周期方法- 函数组件是纯函数,靠 Hooks 管理状态和副作用
- Fiber 架构下推荐函数组件(更好的性能和灵活性)
Q3: 为什么 Hooks 不能在条件/循环/嵌套函数中调用?
答: Hooks 依赖链表顺序来关联 state 和 hook。每次渲染时,React 按调用顺序读取链表中对应位置的值。如果顺序变了,React 就无法正确找到对应的 state,导致 bug。
Q4: createElement 和 cloneElement 的区别?
答:
createElement是"从零创建"一个元素cloneElement是"复制"一个已有元素,可以修改其中的属性
Q5: useState 和 useRef 的区别?
答:
useState会触发组件重渲染useRef修改 current 不会触发重渲染,常用于存 DOM 引用或不需要渲染的值
⚠️ Day 1 能应付多少面试?
✅ 能回答的问题
| 问题 | 能不能答 |
|---|---|
| React 有哪些核心包?各有啥职责? | ✅ 没问题 |
| 函数组件 vs Class 组件的区别? | ✅ 没问题 |
| 常用 API 的作用(createElement、Children、memo...) | ✅ 没问题 |
| createElement vs cloneElement? | ✅ 没问题 |
| useState vs useRef 的区别? | ✅ 没问题 |
❌ 扛不住的问题(需要后续 Day)
| 问题 | 需要Day几 |
|---|---|
| Hooks 原理?什么是链表?闭包问题? | Day 2 |
| Fiber 是什么?解决了什么问题? | Day 3 |
| React 渲染流程?render vs commit? | Day 3 |
| Diff 算法怎么工作的?key 为啥不能用 index? | Day 4 |
| useEffect 清理函数什么时候执行? | Day 2 |
| scheduler 调度原理?优先级咋回事? | Day 3 |
| React 18 并发模式?startTransition 干嘛的? | Day 5 |
📊 学习建议
| 你的目标 | 需要学到哪 |
|---|---|
| 初级/中级前端 | Day 1-2 够用 |
| 资深前端 | 至少 Day 1-4 |
| 面试造火箭 | 6 周全学完 |
🔗 关联文件
-
packages/react/index.js- 入口文件 ✅ -
packages/react/src/ReactClient.js- API 导出 ✅ -
packages/react/src/ReactBaseClasses.js- Class 组件 ✅ -
packages/react/src/ReactHooks.js- Hooks 入口 ✅ -
packages/react/src/jsx/ReactJSXElement.js- createElement ✅
📝 今日自查
- 能说出 react、react-dom、reconciler 的关系
- 熟悉所有常用 API 分类
- 能解释 JSX 转译流程(Day 2 会深入)
- 能回答基础面试题
⏭️ 下节预告
Day 2:JSX 转换原理
- JSX 是如何变成 createElement 的?
- 为什么要用 JSX?直接用 createElement 行不行?
- React Element 和 DOM Element 的区别?
有问题?问我! 🧙♂️