1. React面试到底在考什么先想清楚面试官的出题逻辑说实话我面过不少候选人也被面过。站在面试官的角度看React题目的核心从来不是你有没有背过八股文而是要在半小时到一个小时内判断出你在这个框架上的真实积累到什么程度。这个判断不是靠一两道说说useEffect和useLayoutEffect的区别就能完成的面试官手里其实有一条隐形的分层逻辑。第一层是会用你写过React项目知道JSX怎么写、组件怎么拆、props和state怎么传项目能跑起来。这一层对应的是初级岗位题目通常停留在React有哪些生命周期、useState和setState有什么区别这种可以直接背答案的层面。第二层是理解你能说清React的渲染流程、虚拟DOM的作用、为什么会有协调reconciliation这个过程能主动说出不推荐在useEffect里修改state导致死循环背后的原因。第三层是驾驭你明白Fiber架构解决了什么问题、并发渲染Concurrent Rendering到底是什么、怎么用React的调度机制去设计一个真正流畅的交互。到了这一层面试官给你的通常就不是背诵题了而是场景题——给一段会出现性能问题的代码让你找出问题并优化再追问一句如果状态变多了怎么办。所以这篇文章我不会只罗列答案而是把React面试题按这条逻辑线拆开来讲。每个章节里我会先给出我作为面试官会考察什么然后再给一个可以直接照着组织语言的回答框架。你已经理解了React的基础使用但想在面试里更有底气或者准备从业务开发转到底层原理研究这篇文章都适合你。2. Hooks与组件通信高频基础题的正确打开方式2.1 问为什么用Hooks其实在问你对状态逻辑复用的理解很多候选人一上来就背类组件有this绑定问题、生命周期逻辑分散、代码复用困难。这种回答没错但只是表面。我一般会追问一句那你觉得Hooks是怎么解决这些问题的这时候能答上来的人就明显少了一半。这里的关键词是状态逻辑复用。在类组件时代我们复用状态逻辑主要靠高阶组件HOC和渲染属性Render Props。HOC的问题在于会产生多余的组件层级调试的时候Redux DevTools里能看到一堆因为withRouter、connect包裹而产生的嵌套组件每次渲染都要多经过几层Props传递。Render Props则容易导致写出来的JSX缩进地狱代码可读性很差。Hooks的设计思路完全不同它把状态本身和使用状态的组件解耦了你写一个useXXX开头的函数里面用useState、useEffect、useRef组合出你要的逻辑然后在任何组件里直接调用它状态逻辑就像搭积木一样被组合进去而不是包裹在组件外面。我在面试里比较欣赏的回答会是这样先说Hooks带来的三个直接好处——消灭this绑定问题、让生命周期按关注点聚合而不是按时机分散、自定义Hooks让状态逻辑真正可复用然后补一句Hooks的实现依赖Fiber节点上的链表结构每次渲染都要保证Hook的调用顺序一致所以React规定了不能把Hook放在条件语句里。这一句话就能把面试官从会不会用带到懂不懂实现因为Hook的顺序依赖是React文档里明确强调的规则但能说清为什么有这个规则的人不多。2.2 useEffect、useLayoutEffect、useInsertionEffect三兄弟的区别怎么讲才显得你理解深刻直接背三者的执行时机是最基础的答法useEffect在浏览器绘制之后异步执行useLayoutEffect在DOM变更后、浏览器绘制之前同步执行useInsertionEffect在DOM变更之前执行主要用于CSS-in-JS库插入样式。但面试官真正想听的是你什么场景下会用到它们以及为什么这个时机很重要。我建议用副作用Side Effect与视觉一致性这条主线来串。useEffect的异步执行特性意味着它不会阻塞浏览器渲染所以数据请求、埋点上报、日志统计这类和用户看到的画面没有直接关系的操作都应该放在useEffect里。useLayoutEffect的同步特性意味着在浏览器绘制前你已经完成了DOM测量或者布局调整比如你在页面上做一个Tooltip组件需要读取目标元素的尺寸和位置来定位气泡如果放在useEffect里用户可能在极短的瞬间看到气泡出现在错误位置然后又被修正这就是闪烁。把这类操作放到useLayoutEffect里浏览器根本不会绘制那个错误位置视觉上是无缝的。还有一类特别容易被忽略的配合第三方非React库时比如在React组件里初始化一个jQuery插件或D3图表一定要用useLayoutEffect或者在class组件里用componentDidMount而不是useEffect。我踩过这个坑——在useEffect里初始化了一个需要测量容器尺寸的图表库结果第一次渲染时容器宽度是0图表画出来是缩在一起的刷新页面偶尔才对。后来意识到是useEffect的绘制后执行时机导致的改成useLayoutEffect就稳定了。这个例子我在面试里讲过几次面试官普遍反馈这才是有实际项目经验的人能说出来的话。2.3 组件通信的完整地图从props到Context再到状态管理React组件通信是几乎必考的基础题但很多人的回答是零散的父子用props子父用回调函数兄弟用状态提升跨层级用Context或Redux。这么说没错但我会问一个更细的问题如果一个组件树特别深中间某一层根本不需要用到数据你怎么把数据从顶层传到最底层的兄弟组件这个问题的标准进阶答案思路是Cartesian Props逐层透传的问题在于中间组件被迫承载无意义的props组件就失去了封装性。Context可以解决透传问题但它有一个隐蔽的性能特性Context的值变化时所有消费该Context的组件都会重新渲染即使它们只用到了其中一部分数据。所以后来社区才出现了很多优化方案比如把Context拆成数据Context和更新函数Context两个或者用useSelector之类的方式在原子层面订阅。Redux/Zustand这类方案能流行本质上就是为了解决跨组件共享状态和响应式更新这对矛盾。我个人的经验法是组件通信先想清楚数据流向。父传子永远是最简单可预测的优先用。兄弟组件需要共享数据时先想想状态提升到最近的共同父组件是否会让这个父组件变得臃肿——如果只是两三个组件共享一个简单的布尔值或字符串提升就够了如果状态结构复杂、更新频繁、被很多组件消费才考虑Context或外部状态库。面试时如果能把这个决策过程讲出来比单纯列方案强得多。3. 渲染机制与性能优化在原理层面拉开差距3.1 Virtual DOM做得再好也不如少渲染来得实在说说虚拟DOM和diff算法是React面试的经典题了。多数人的回答是虚拟DOM是一个JS对象描述真实DOM结构每次状态更新都会生成新的虚拟DOM通过diff找出差异然后只更新变化的部分。这个答案能拿基础分但拿不到高分。我会反着讲这个问题虚拟DOM的价值不在于比直接操作DOM快而在于把哪些节点需要更新这个计算过程从浏览器搬到JavaScript里用可预测的方式去组织。真实DOM操作确实慢但现代浏览器引擎对DOM的优化已经很强了所以虚拟DOM真正的意义是提供了一个最小化DOM操作的计算框架。diff算法的核心逻辑也不复杂同层比较、每层遍历、通过key识别节点是否复用。但能说清楚为什么elementType变了就直接重建整棵子树——因为React认为如果组件类型变了子树内的内容大概率也变了直接重建比继续深入diff更划算——这才是理解。不过我在真实项目中体会最深的一点是性能问题大部分时候不是diff慢而是根本不该触发diff的组件也被触发了。父组件每次setState默认情况下所有子组件都会重新执行render函数生成虚拟DOM参与diff。如果子组件是一个渲染成本不低的表格或图表这个浪费就很明显。所以性能优化的第一优先级永远是减少无意义的渲染用React.memo包裹纯展示组件、用useMemo缓存计算结果、用useCallback稳定函数引用三件套用好了比纠结diff算法快多少重要得多。3.2 别滥用useMemo和useCallback面试官真正想看的优化判断力很多候选人会在简历上写熟悉React性能优化然后面试的时候说我在项目里用了大量useMemo和useCallback。这句话在我这里是减分项因为大量使用往往意味着没有理解这两个API的适用边界。useMemo的价值是缓存昂贵的计算结果但React官方文档都承认判断计算是否昂贵本身就是个难题。我见过有人把一个1 2的加法用useMemo包起来这完全是负优化。更隐蔽的问题是如果useMemo的依赖数组写得不合理比如依赖了一个每次渲染都变化的引用类型对象那它根本不会生效反而增加了每次渲染时做依赖比对的开销。useCallback也一样它的真实价值不是让函数不重新创建一个函数对象的创建成本极低而是让子组件的React.memo能真正生效——因为React.memo做的是浅比较只要props里有任何引用类型变了memo就失效了所以传给子组件的回调函数必须保持引用稳定。面试时我喜欢说一句useMemo和useCallback是性能优化的最后一步不是第一步。第一步永远是先问这个组件真的需要重新渲染吗能不能把state往下放、把组件拆分得更细能说出这个判断顺序的人说明他真的在大型项目里打磨过而不是只会套API。3.3 从Fiber架构到并发渲染这个知识点能让你进二面如果说前面的内容是React面试的及格线那Fiber和Concurrent Rendering就是拉开差距的加分区。面试官在这个环节想确认两件事第一你是否真的看过React的源码或至少深入读过官方文档的进阶部分第二你是否能理解渲染不只是改DOM这么简单这几个字背后的复杂度。Fiber可以理解为一个虚拟DOM的升级版数据结构——每个组件对应一个Fiber节点节点之间用链表连接形成一棵Fiber树。之所以要用链表是因为React的渲染过程需要可中断一次渲染可以拆成很多个小的任务单元每处理完一个Fiber节点就回头看看浏览器有没有更紧急的事情比如用户输入要做有就让出主线程知