最近换工作,面了几家公司,被问了很多React相关的问题。有些是基础题,有些是深入原理的,还有些是结合实际项目的。
这篇文章记录一下我被问到的那些React 18+生态的面试题,以及我的回答思路。如果你也在准备前端面试,希望能帮到你。
Concurrent Mode相关
第一个被问到的就是React 18的Concurrent Mode(并发模式)。
面试官问:React 18的并发模式和之前的模式有什么区别?它解决了什么问题?
我的回答是:React 18之前的渲染是同步的,一旦开始渲染就不能中断,直到渲染完成。如果组件树很大,渲染时间长,就会阻塞主线程,导致用户交互卡顿。并发模式把渲染变成了可中断的,React可以在渲染过程中暂停,去处理更高优先级的任务,比如用户输入,然后再回来继续渲染。这样用户交互就不会卡顿了。
然后面试官追问:并发模式下,render函数可能会被调用多次,这会带来什么问题?怎么解决?
这个问题我答得不太好。我说的是render函数应该是纯函数,不能有副作用,因为可能被调用多次。如果有副作用,比如在render里修改外部变量,就会出问题。解决方法是把副作用放到useEffect里,或者用useMemo缓存计算结果。
后来想了想,还应该提到严格模式下的双重调用,以及useTransition和useDeferredValue的使用场景。
Suspense相关
第二个高频问题是Suspense。
面试官问:Suspense的工作原理是什么?它和错误边界有什么区别?
我的回答是:Suspense是React提供的一种声明式的数据加载方式。组件在等待数据加载的时候,可以"挂起",React会显示fallback内容,等数据加载完成后再渲染组件。Suspense的原理是组件在渲染过程中抛出一个Promise,React捕获到这个Promise,就知道组件在等待数据,然后显示fallback。等Promise resolve之后,React重新渲染组件。
Suspense和错误边界的区别是:Suspense处理的是加载状态(Promise pending),错误边界处理的是错误状态(Promise reject或渲染错误)。两者可以配合使用,Suspense处理加载中,错误边界处理加载失败。
然后面试官问:Suspense在服务端渲染中有什么作用?
这个问题我答得还可以。我说的是React 18的服务端渲染支持流式渲染和选择性注水。以前的SSR是先生成完整的HTML再发给客户端,客户端拿到HTML之后再注水。如果某个组件数据加载慢,整个页面的渲染都会被阻塞。有了Suspense之后,可以先把已经准备好的部分发给客户端,慢的部分先显示fallback,等数据准备好了再流式发送过去,然后单独注水。这样首屏时间大大缩短。
状态管理相关
状态管理是面试必问题。
面试官问:React 18之后,你会选择什么状态管理方案?为什么?
我的回答是:看项目规模和团队情况。小项目用React自带的useState和useContext就够了,不需要引入额外的库。中等项目可以用Zustand,轻量、简单、TypeScript支持好,比Redux简单很多。大项目或者团队已经习惯Redux的,可以用Redux Toolkit,比原生Redux好用很多,减少了很多样板代码。
然后面试官问:useContext有什么性能问题?怎么解决?
这个问题很经典。我说的是useContext的问题是,只要Context的值变了,所有消费这个Context的组件都会重新渲染,不管用到的值有没有变。如果Context里放了很多不相关的值,一个值变了所有组件都要重渲染,性能就差了。
解决方法有几个:一是拆分Context,把不相关的状态放到不同的Context里;二是用useMemo包裹Context的value,只有依赖变了才更新;三是用选择器模式,比如use-context-selector库,让组件只订阅自己需要的值;四是把状态放到专门的状态管理库里,比如Zustand,它天生支持选择器。
性能优化相关
性能优化也是高频考点。
面试官问:React 18有哪些性能优化的手段?
我的回答是:第一是用useMemo和useCallback缓存计算结果和函数引用,避免不必要的重渲染。第二是用React.memo包裹组件,只有props变了才重新渲染。第三是用虚拟列表处理长列表,只渲染可视区域的内容。第四是用useTransition把非紧急的更新标记为过渡更新,让紧急更新优先执行。第五是用useDeferredValue延迟处理某些值,让界面保持响应。第六是代码分割,用React.lazy和Suspense按需加载组件。
然后面试官问:useTransition和useDeferredValue有什么区别?
这个问题我答得还可以。我说的是useTransition是用来包裹更新函数的,你主动把某个更新标记为过渡更新。useDeferredValue是用来包裹一个值的,让这个值的更新延迟执行。简单说,useTransition是从更新的源头控制,useDeferredValue是从消费的地方控制。两者都能让界面保持响应,但使用场景不同。
Hooks相关
Hooks的问题也很多。
面试官问:useEffect和useLayoutEffect有什么区别?什么场景下用useLayoutEffect?
我的回答是:useEffect是异步执行的,在浏览器绘制之后执行,不会阻塞渲染。useLayoutEffect是同步执行的,在DOM更新之后、浏览器绘制之前执行,会阻塞渲染。大部分情况下用useEffect就够了。只有当你需要在浏览器绘制之前修改DOM,比如测量元素尺寸、调整滚动位置,这时候用useLayoutEffect,因为如果用useEffect,用户会看到一瞬间的错误状态。
然后面试官问:useEffect的依赖数组里放对象或函数会有什么问题?怎么解决?
这个问题也很经典。我说的是对象和函数是引用类型,每次渲染都会创建新的引用,导致useEffect每次渲染都执行。解决方法是用useMemo包裹对象,用useCallback包裹函数,或者把对象/函数放到组件外面(如果不依赖组件状态的话)。还有一种方法是用useRef保存最新的值,在effect里读取ref.current,这样就不需要把值放到依赖数组里了。
服务端渲染相关
如果简历里写了SSR,一定会被问到。
面试官问:React 18的SSR和之前有什么区别?
我的回答是:React 18之前的SSR是一次性的,服务端生成完整HTML,客户端一次性注水。React 18支持流式SSR和选择性注水。流式SSR是服务端可以边生成边发送HTML,不用等所有组件都准备好。选择性注水是客户端可以先对已经发送的部分注水,不需要等整个HTML都到齐。这样首屏时间大大缩短,用户体验更好。
然后面试官问:SSR有什么优缺点?
我的回答是:优点是首屏加载快,SEO友好,特别是对内容型网站。缺点是服务端压力大,开发复杂度高,有些浏览器API在服务端用不了,需要做兼容。还有就是如果数据加载慢,SSR反而会更慢,因为服务端要等数据加载完才能生成HTML。React 18的Suspense SSR就是为了解决这个问题的。
工程化相关
除了React本身,生态工具也会被问到。
面试官问:你在项目中用了哪些React生态的工具?为什么选择它们?
我的回答是:构建工具用Vite,比Webpack快很多,开发体验好。状态管理用Zustand,轻量简单。路由用React Router,最主流的方案。UI组件库用Ant Design,企业级项目用着省心。测试用Vitest和React Testing Library,Vite原生支持,配置简单。类型检查用TypeScript,现在项目基本都用TS了。
然后面试官问:Vite和Webpack有什么区别?为什么Vite更快?
这个问题我答得还可以。我说的是Webpack是基于打包的,开发的时候要把所有模块打包成一个bundle,项目大了启动就慢。Vite是基于原生ES模块的,开发的时候不需要打包,浏览器直接请求模块,启动速度和项目规模无关。Vite用esbuild做预构建,速度比Webpack的babel快很多。生产构建Vite用Rollup,打包质量也很好。
面试心得
面了几家公司,总结了一些心得。
第一是基础要扎实。React的原理、Hooks的机制、渲染流程,这些基础东西一定要懂。面试官不会只问API怎么用,更关心你懂不懂原理。
第二是要有项目经验。光说理论没用,面试官会问你在项目中怎么用的,遇到了什么问题,怎么解决的。所以平时做项目的时候要多思考,多总结。
第三是要关注新版本。React 18的并发模式、Suspense、自动批处理这些新特性,面试官很喜欢问。要保持学习,了解新版本的变化。
第四是要会表达。技术好是一方面,能不能说清楚是另一方面。面试的时候要条理清晰,先给结论,再展开说,不要东拉西扯。
第五是不要怕说不知道。遇到不会的问题,坦诚说不知道,然后说说你的思路,比瞎编强。面试官更看重你的思考过程,而不是标准答案。
写在最后
React的生态发展很快,新东西层出不穷。面试的时候被问到不会的很正常,关键是要有学习能力和解决问题的能力。
这篇文章记录的只是我被问到的一部分问题,不同公司、不同岗位的面试重点不一样。但核心的东西是相通的,把基础打牢,把原理搞懂,以不变应万变。
希望这篇文章能帮到正在准备面试的你。祝大家都能拿到心仪的offer。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录