React 源码学习 Day 1:React 包入口与核心 API

1 阅读7分钟

React 源码学习 Day 1:React 包入口与核心 API

日期:2026-03-17 目标:理解 React 整体架构,掌握核心 API 适合:初级/中级前端,资深面试打基础


📌 本章总结

1. React 核心包架构

包名职责
react核心 API(React.xxx)
react-domDOM 渲染器
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作用栗子
ComponentClass 组件基类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创建 Contextconst ctx = createContext(默认值)
useContext消费 ContextuseContext(ctx)
forwardRef转发 refforwardRef((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 元素渲染成真实 DOM
  • react-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 的区别?

有问题?问我! 🧙‍♂️