React Mind Map
架构
Fiber 是什么,怎么实现的?
在 React 15 之前更新 VDom 树时,React 会找到所有差异并一次性地同步更新它们,这可能会导致卡顿。使用 Fiber 后,React 把树的遍历转换为从父节点,到子节点,到兄弟节点,再回到父节点这一流程, 使得遍历是可以中断的,分批异步更新 DOM 成为可能。React 可以在浏览器有动画、用户输入任务等高优先级任务时,先执行高优先级任务。直到有空闲才继续执行 Diff 操作。
const tasks = []
function diff (time) {
while (time > 0 & task.length) {
doHighLevelWork()
}
if (task.length) {
requestIdelCallBack(diff)
}
}
requestIdelCallBack(render)
SyntheticEvent 是什么?
合成事件(SyntheticEvent)是 React 17 以前的事件系统设计思路,它是 DOM 事件上的一层封装。合成事件解决了浏览器兼容性问题,并且通过池化技术减少了内存消耗。不过,池化技术会在事件回收时,将对象属性重置为空, 所以合成事件用于异步时,需要提前将值缓存或者使用 event.persist 将事件持久化。
PureComponent 是怎么更新视图的?
React.PureComponent 中默认在 shouldComponentUpdate 中实现了 state 和 props 的浅比较,以达到性能提升的目的。
受控组件和非受控组件有什么区别?
受控组件通过绑定 value state 以及 onChange 事件来控制表单的状态,使用 HOC 能轻松创建受控组件。非受控组件则直接通过 ref 拿到表单的值。后者的代码要简单不少。
React 的生命周期是怎么样的?
组件的生命周期分为 Mount、Update 和 Unmount 三个阶段,每个阶段又可以划分出渲染、预提交和提交三个过程。未挂载的组件会在实例创建好后,调 render 函数更新 DOM 节点, 最后触发 componentDidMount 钩子,组件更新时则是根据 props 和 states 通过 shouldComponentUpdate 钩子来判断是否要调用 render 函数。

getDerivedStateFromProps 是做什么的?
getDerivedStateFromProps 是 React 16.3 后引入的生命周期,用来替代 componentWillReceiveProps。它用来接受来自 props 的更新,以更新当前组件的 States。 在 getDerivedStateFromProps 之后,states 和 props 的改变都会走 shouldComponentUpdate 来确定是否跳过渲染。
static getDerivedStateFromProps(nextProps, prevState) {
const { type } = nextProps
if (type !== prevState.type) {
return {
type,
}
}
return null
}
为什么在 componentDidMount 而不是其它生命周期中取请求数据?
其它生命周期可能执行多次,比如说 componentWillMount 会在服务端和客户端各执行一次。
类组件和函数组件有什么不同?
类组件是面向对象的,函数组件是函数式的,了解前者主要需要知道继承、生命周期概念,后者则是 state、无副作用和引用透明。类组件依靠 shouldComponentUpdate 来优化性能,函数组件使用 React.memo。
setState 是同步的还是异步的?
在大部分情况下 setState 是异步的。多次 setState 的状态会被合并,直到当前宏任务跑完才会触发重新渲染。这和 Taro 的思路是一致的。
React 和 Vue 有什么异同?
相同的地方在于他们都是渐进式的框架,都有自己的脚手架和最佳实践的模板;在框架层面,数据流都是自顶向下的,都引入了 VDom 的概念。
不同的地方在于 Vue 的数据和视图是双向绑定的,React 推崇不可变数据,并且需要手动优化,所以 Vue 模板有利于静态优化。
React 的演进历程是什么?
React 经历了多次架构变革。早期使用 mixins 实现代码复用,ES6 类组件出现后转为 class-based, 直到 React 16.8 引入 Hooks 才真正改变了开发范式。Hooks 将必要样板代码减少了一个数量级, 并催生了自定义 Hook 的爆发式发展。第三方库也经历了类似轨迹:Redux 等方案兴起后被抛弃, 因为开发者意识到样板代码的成本得不偿失。
2026 年回看:React 18/19 的 Concurrent Mode 与 Server Components 延续了"减少样板代码"的承诺, 却意外引发了社区对"复杂性爆炸"的广泛批评。2025 年 Redux 作者 Mark Erikson 承认 React 生态 "支离破碎",RSC 的落地困难与 Vercel 影响力争议印证了 2022 年对 React 方向的担忧。 不过 React 仍占约 48.7% 市场份额,其成熟度在企业级场景难以被撼动。
见:Get in Zoomer, We're Saving React
多层 Context Provider 嵌套是否等同于多个局部 store?
多层 Context Provider 嵌套常被用来替代全局状态管理方案,但这种做法本质上是把单一全局 store 拆成多个作用域更小的局部 store,只是换了一个叫 context 的名字。它没有减少状态管理的复杂度, 只是把 store 的边界沿着组件树做了重新划分。
Redux 因样板代码较多而在社区中受到批评,导致一些本应放在 Thunk 或 Saga 中的异步逻辑和副作用被硬塞进组件 render() 中,反而让视图层承担了本不该承担的控制职责。正确做法是把副作用留在 store 层, 让组件只负责渲染。
Thunk 和 Saga 如何隔离 React 副作用?
Redux Thunk 和 Redux Saga 都是把副作用从组件 render() 中抽离到 store 层的方案,但抽象层级不同。
Thunk 让 action creator 返回一个函数,Redux Thunk 中间件拦截这个函数并注入 dispatch 和 getState,使它能够执行异步操作后再 dispatch 普通 action:
function incrementAsync() {
return (dispatch, getState) => {
setTimeout(() => {
dispatch({ type: 'INCREMENT' })
}, 1000)
}
}
store.dispatch(incrementAsync())
Saga 则使用 ES6 Generator 函数和声明式 Effect 来描述异步流程。call 表示调用函数,put 表示 dispatch action,takeEvery 表示监听某类 action:
import { call, put, takeEvery } from 'redux-saga/effects'
function* fetchUser(action) {
try {
const user = yield call(api.fetchUser, action.payload)
yield put({ type: 'FETCH_USER_SUCCESS', payload: user })
} catch (e) {
yield put({ type: 'FETCH_USER_FAIL', payload: e.message })
}
}
function* watchFetchUser() {
yield takeEvery('FETCH_USER_REQUEST', fetchUser)
}
Thunk 适合简单异步和条件 dispatch;Saga 适合复杂流程、竞态、取消和长时间运行的副作用。现代 Redux Toolkit 已内置 Thunk,复杂场景仍可引入 Saga 或 RTK Query。
Hooks
应用
什么是 HOC?
高阶组件是一种使用 React 组件的组合特性,把某种组件转换成另一种组件的设计模式,可以用来处理有横切关系的组件。
什么是组件横切关系?
横切关系是那些和程序中大部分模块都有联系的部分,它们形成了切面开发的基础单元。