1. 面经

JSX语法转换

react JSX 语法转换 (JSX => React.createElement => 虚拟Dom对象 => 真实DOM)

参考:https://pomb.us/build-your-own-react/

  • 字节|React 里组件间怎么通信的(集中式,消息发布-订阅,父子单向流通)
  • [ ] 字节|React.createElement(name, props, children)(JSX转换)
    JSON 数据渲染为DOM,函数格式为
    可以使用 React.createElement(name, props, children)。不需要运行,主要看思路。可以一行写完
    https://www.nowcoder.com/discuss/762363?type=post&order=create&pos=&page=0&ncTraceId=&channel=-1&source_id=search_post_nctrack

    1. function tree(props){
    2. return DOMLElement
    3. }
    1. function tree(props){
    2. return React.createElement(props.name, props.props, props.children.map( child => tree()))
    3. }
  • [ ] 小米|react和原生的区别

  • 小米|diff算法原理
  • 小米|为什么 key值改变 就会重新渲染 diff算法是如何识别key值得
  • 字节|React 的异步加载和懒引入原理
  • 北森|react的生命周期,具体说一下生命周期的过程
  • 北森|react新增的几个生命周期
  • 北森|介绍一下setState
  • 北森|setState怎样才是同步的:isBatchingUpdates属性设为false则是同步;异步: 在 React 可以控制的地方,就为 true,比如在 React 生命周期事件和合成事件中,都会走合并操作,延迟更新的策略。同步: 在 React 无法控制的地方,比如原生事件,具体就是在 addEventListener 、setTimeout、setInterval 等事件中,就只能同步更新。
  • 4399|React中如何实现生命周期?
  • 4399|React中如何实现拦截器
  • 小红书|react的生命周期(忘记了 getDerivedStateFromProps 。。。。),发送请求是哪个函数?
  • 小红书|react的性能优化?(面试官不太满意)
  • 小红书|虚拟dom(简单讲了一下diff算法,没答好),key的用途?
  • 小红书|react的共享全局数据如何实现的?
  • 阿里|React 18 Suspense在SSR的作用
  • 美团|讲讲diff算法
  • 美团|React是怎么渲染的
  • 微步在线|react JSX 语法转换 (JSX => React.createElement => 虚拟Dom对象 => 真实DOM)
  • 微步在线|react stack React 16 前的底层架构
  • 游卡|react 用哪个版本
  • 游卡|自己封装组件如何做 ( 高阶组件吗…hhh)
  • 阿里|为什么要用Virtul Dom
  • 阿里|Virtual Dom的效率一定比直接操作Dom高吗
  • 阿里|你说Virtual Dom减少不必要的Dom操作,让你来设计如何实现这个效果
  • 新奥|React中unicode和 ??(没听到)的差别,以及unicode的字符转换
  • 新奥|setState的同步异步情况,为什么在setTimeOut中就是同步立即触发的。同样都是调用setState是如何区分的。(我说了在react生命周期和合成事件中是是异步,在setTime setInterval中是同步的。原因是会通过isbatchingupdate判断进入队列还是立即更新。面试官说再想一下怎么判断是立即更新还是批量更新)
  • 新奥|react的错误边界
  • 新奥|react的项目的整体的优化。(我说了1.渲染列表的key值2.memo,PureComponent, shouldComponentUpdate 缓存3.多个State合并4.组件的逻辑复用抽离5.资源包使用CDN引入6.使用文档碎片7.打包优化等等。面试官说你说的这些是代码优化再想想项目优化)
  • AfterShip|一道场景题:有一个组件,传入 props,当 props 发生改变的时候,执行一个函数 。我说使用在生命周期函数 componentDidUpdate 的时候触发。面试官问,在 react hook 中怎么实现。我的回答是将 props 作为 useCallback 的依赖项,面试官说不对。后来他说一种最简单的方法是将 props 使用 state 存起来,之后再 useEffect 的时候,比较后面传入的 props,进行触发。
  • 字节|React解决了什么问题
  • 字节|react事件绑定的方式以及其区别
  • 顺丰|react合成事件的优点,事件绑定在哪里
  • 顺丰|react组件间通信的方式
  • 顺丰|react单向数据流的理解
  • 顺丰|react的diff算法
  • 绿盟|react state怎么修改
  • 绿盟|组件间通信 怎么修改子组件的props
  • [ ] 字节|实现react render函数

    1. function render( vnode, container ) {
    2. // 当vnode为字符串时,渲染结果是一段文本
    3. if ( typeof vnode === 'string' ) {
    4. const textNode = document.createTextNode( vnode );
    5. return container.appendChild( textNode );
    6. }
    7. const dom = document.createElement( vnode.tag );
    8. if ( vnode.attrs ) {
    9. Object.keys( vnode.attrs ).forEach( key => {
    10. if ( key === 'className' ) key = 'class'; // 当属性名为className时,改回class
    11. dom.setAttribute( key, vnode.attrs[ key ] )
    12. } );
    13. }
    14. vnode.children.forEach( child => render( child, dom ) ); // 递归渲染子节点
    15. return container.appendChild( dom ); // 将渲染结果挂载到真正的DOM上
    16. }
    17. babel 转义后代码什么样子
  • [ ] 字节|react15 16 差异 生命周期的变化以及为什么会变?react fiber

  • 字节|合成事件机制?(回答了事件委托以及 react 是如何模拟事件冒泡以及事件捕获的。)
  • 字节|react组件间通信
  • 字节|react key 有了解吗
  • 字节|react为什么要用setState()
  • 字节|react ref用法
  • 百度|react事件绑定原理?

  • 京东|react的hooks与vue的hooks区别
  • 恒生|vue和react的区别
  • 恒生|vue和reactdiff算法不同,具体是哪里不同
  • 东方财富|说说Vue的双向数据流和React的单向数据流是怎样实现的? Vue:Observer 、 Dep 、Watch Vue2通过defineProperty,Vue3通过Proxy React:balabala~
  • 东方财富|Vue和React中都用了虚拟DOM,你说下什么是虚拟DOM,两个框架间有什么区别?
  • 东方财富|Vue和React中组件间数据传递有哪些常用的? props emit provide inject eventBus Vuex React: props context redux…..
  • 东方财富|Vue和React使用过哪些优化手段? vue:v-for加key v-for v-if不一起使用。。。 react: shouldComponentUpdate、PureComponent、memo
  • 赛码网|Redux 与 Vuex
  • 招银网络科技|vue和react我看你都写在项目经历上了,你能说下两者的数据流的区别么?以及好处? vue 双向 react单向,单向更容易最终数据变化的源头,出现错误时容易定位,双向绑定快捷方便,不需要自己触发依赖项的修改,Vue使用时能精准的感知到数据的变化以及需要通知的地方,因而Vue1.x中并未采用虚拟dom,直接以这种方式精准感知需要通知改变的依赖项,但是内存消耗较大,故Vue2折中引入了虚拟dom。
  • 招银网络科技|一般你们使用的性能优化手段都有哪些呢? 打包、缓存、gzip、图片压缩、css属性(transform不会触发reflow。。,还有默认硬件加速)、css选择器(层级不要太深)、框架层级的如react的pureComponent、shouldComponentUpdate、memo,vue的v-if/v-for不一起用、路由懒加载等等。。

  • 字节|react router
  • 小米|react—router- dom,说说它实现原理和常用组件
  • 字节|前端路由原理?

  • 小红书|扯到了redux,于是为什么使用mobx?(笑死,说了实习公司就用的mobx)
  • 阿里|从Redux到Recoil 为什么?讲一下Recoil和他的原理
  • 游卡|状态管理 (说了redux 大概讲了将使用流程 =w= 没深问 我就没有细说)
  • 字节|redux的构成
  • 转转|讲一下reduxredux数据是怎么渲染到组件的
  • 绿盟|redux原理
  • 字节|redux工作流程

  • 阿里云|react hooks 在使用时有哪些常见的坑
  • 字节|React useCallback 什么作用(memorize函数,重新渲染时不会重新定义,但会重新运行。我之前的理解是错的,以为是 useMemo 的函数版)
  • 字节|react hooks
  • [ ] 字节|看结果

    1. function Index(){
    2. const [ num ,setNumber ] = React.useState(0)
    3. const handerClick=()=>{
    4. for(let i=0; i<5;i++ ){
    5. setTimeout(() => {
    6. setNumber(num+1)
    7. console.log(num)
    8. }, 1000)
    9. }
    10. }
    11. return <button onClick={ handerClick } >{ num }</button>
    12. }
  • [ ] 字节|useMemo和useCallback的区别,以及用法

  • [ ] 字节|问一个react hook题目, 假如有一个这样的组件:
    现已知上述代码不安全, 如果第 5 行执行时间过长, 在第 5 行阻塞时, 页面被用户手动关闭了, 再执行第 6 行时会报 warning, 请问如何改造代码, 使setCount变得安全?
    答: 用 useRef 记录组件的生存状态, 在 useEffect 当中写 return 函数, 组件卸载时将 useRef 记录的状态转为 false

    1. const Demo: FC = () => {
    2. const [count, setCount] = useState(0);
    3. // 组件初始化时执行该函数, 向后端请求 count 的初始值
    4. const init = async () => {
    5. const value = await getCount(); // 向后端发出请求获取初始数据
    6. setCount(value); // 不安全
    7. }
    8. useEffect(() => {
    9. init();
    10. });
    11. // return ...;
    12. }
  • [ ] 北森|对hooks的了解

  • 北森|介绍一下useEffect
  • 4399|React的hooks比class写法优势在哪?
  • 小红书|react hook的原理,及对比类组件有什么好处?react自定义hook如何使用生命周期函数?
  • 小红书|useState是怎么实现的?(就记得是某hook的语法糖,但忘记具体哪个hook了,该死。。。。),它的返回值是什么?
  • 微步在线|react Hooks (用到了哪写hooks)
  • 游卡|为什么会诞生hooks class 不是挺好用的吗? (我认为是函数式编程)
  • 游卡|useCallback 和 useMemo 区别
  • 游卡|useReduce (没有用过…)
  • 顺丰|react hook的优点(常问)
  • 顺丰|react的useState的原理
  • 转转|讲一下useEffect
  • 绿盟|hooks讲一讲
  • 字节|简单讲讲对 hooks 的理解
  • 字节|Hooks 如何模拟类组件生命周期
  • 字节|实现 useMemo
  • 字节|实现 useScroll
  • 字节|hook 的实现原理? (我说 hook 类似细粒度的 fiber,解释了一下几个 api 的主要实现,提了一下 useLayoutEffect 和 useEffect 的执行时机的差异等等,然后重点说了 hook 的意义。)

  • 阿里云|讲讲 fiber
  • 微信|React原理知道吗
    • for循环setState 页面会怎么展示?为什么
    • React是怎么做batch update的吗 讲讲原理
    • 如果一个frame完成不了计算会怎么办?
    • ⇒讲讲事件循环
    • Fiber Fiber是怎么知道回来要执行的任务?
  • 微信|React源码看过吗
    • 讲了react18的源码 正好看了useTransition
    • Fiber的原理,怎么实现的
    • 怎么打断diff计算的
    • Fiber的副作用
    • 讲讲浏览器的渲染流程
  • 美团|React 讲讲Fiber
  • 微步在线|react fiber (讲了 state 原理)
  • 游卡|fiber (怎么都有问fiber呀 烦烦烦)
  • 字节|react 生命周期 源码相关
  • 字节|react scheduler 调度器如何 polefill requestIdleCallback 这个 api? (这其实就是利用 requestAnimationFrame 这个 api 来维护浏览器每帧的刷新间隔,然后通过 window.postMessage 来安排合适的任务执行时机。)
  • 字节|fiber 的调度过程?(先通过 scheduler 进行异步调度,然后就俩阶段,render 阶段负责调和子节点构建 fiber 树以及 dom 树构建和一些属性的初始化等等等,整体来看是一个递 perforUnitOfWork 和归 completeWork 的过程;commit 阶段不可打断分三部分 before mutation、mutation、layout分别负责不同的任务。)

  • 阿里云|讲讲 fiber
  • 微信|React原理知道吗
    • for循环setState 页面会怎么展示?为什么
    • React是怎么做batch update的吗 讲讲原理
    • 如果一个frame完成不了计算会怎么办?
    • ⇒讲讲事件循环
    • Fiber Fiber是怎么知道回来要执行的任务?
  • 微信|React源码看过吗
    • 讲了react18的源码 正好看了useTransition
    • Fiber的原理,怎么实现的
    • 怎么打断diff计算的
    • Fiber的副作用
    • 讲讲浏览器的渲染流程
  • 美团|React 讲讲Fiber
  • 微步在线|react fiber (讲了 state 原理)
  • 游卡|fiber (怎么都有问fiber呀 烦烦烦)
  • React中是怎么识别函数组件和类组件的(从源码的角度)
  • 字节|为什么 react16 设计了 fiber?(fiber的概念就是纤程,主要是提供一个可打断的异步渲染,设计渲染优先级,这是 react16 和之前版本 react 调和过程的主要区别。)

2. 路由管理

路由管理:就是让页面能够根据URL的变化进行页面的切换,这是前端应用中一个非常重要的机制。

路由的重要性主要体现在两个方面:

  • 路由机制提供了按页面去组织整个应用程序的能力,页面之间的交互可以主要通过URL来进行,从而可以让各个业务功能互相独立,实现逻辑的解耦。
  • URL是用于唯一的定位某个资源的。

实现路由机制的核心逻辑就是根据URL路径这个状态来决定在主内容区域显示什么组件

路由框架要考虑的情况:

  • 路由嵌套
  • URL模式匹配
  • 参数提取

    使用 React Router

  • BrowserRouter:用标准的URL路径去管理路由,比如/my-page1这样的标准URL路径。

  • MemoryRouter:通过内存管理路由。
  • HashRouter:通过hash管理路由。
  • Link:定义一个导航链接,点击时可以无刷新地改变页面URL,从而实现React Router控制的导航。
  • Route:定义一条路有原则,可以指定匹配的路径、要渲染的内容等等。
  • Switch:在默认情况下,所有匹配的Route节点都会被展示,但是Switch标记可以保证只有第一个匹配到的路由才会被渲染。

    使用嵌套路由:实现二级导航页面

    路由层面实现权限控制

    利用前端路由的动态特性。
    动态路由:路由的定义规则可以根据条件进行变化。

3. Redux

React-Redux

useSelector
useDispatch


3. immer

4. React

4.1 setState

4.1.1 setState的执行机制

setState是同步还是异步的?为什么有的时候不能立即拿到更新结果而有的时候可以?

钩子函数和React合成事件中的setState

  • 调用setState不会立即更新
  • 所有组件使用的是同一套更新机制,当所有组件didmount后,父组件didmount,然后执行更新
  • 更新时会把每个组件的更新合并,每个组件只会触发一次更新的生命周期

异步函数和原生事件中的setState

  • 在父组件didmount后执行
  • 调用setState同步(立即)更新

    为什么有时候连续两次setState只有一次生效?

  • 直接传递对象的setState会被合成成一次

  • 使用函数传递state不会被合并

推荐使用方法:在调用setState时使用函数传递state值,在回调函数中获取最新更新后的state。
image.png

4.1.2 合成事件

4.2 Reconciliation(协调)

Diffing算法

对比两棵树时,react首先比较两棵树的根节点。不同类型的根节点元素会有不同的形态。

对比不同类型的元素(根节点)

根节点为不同类型的元素时,react会拆卸原有的树并且建立起新的树。(当一个元素从变成react - 图2会触发一个完整的重建流程)。
当卸载一棵树时,对应的DOM节点也会被销毁。组件实例将执行componentWillUnmount()方法。当建立一棵新的树时,对应的DOM节点会被创建以及插入到DOM中。组件实例将执行UNSAFE_componentWillMount()方法,紧接着componentDidMount()方法。所有与之前的树相关的state也会被销毁。

  1. - <div>
  2. <Counter/>
  3. - </div>
  4. + <span>
  5. <Counter/>
  6. + </span>

当对比以上变更时,react会销毁Counter组件并且重新装载一个新的组件。

对比同一类型的元素

当对比两个相同类型的react元素时,react会保留DOM节点,仅比对及更新有改变的属性。
当更新style属性时,react仅更新有所变更的属性。

  1. - <div style={{color: 'red', fontWeight: 'bold'}} />
  2. + <div style={{color: 'green', fontWeight: 'bold'}} />

通过对比上述两个元素,react知道只需要修改DOM元素上的color样式,无需修改fontWeight
在处理完当前节点之后,react继续对子节点进行递归。

对比同类型的组件元素

当一个组件更新时,组件实例会保持不变,因此可以在不同的渲染时保持state一致。react将更新该组件实例的props以保证与最新的元素保持一致,并且调用该实例的UNSATF_componentWillReceiveProps()UNSAFE_componentWillUpdate()以及componentDidUpdate()方法。
下一步,调用render()方法,diff算法将在之前的结果以及新的结果中进行递归。

对子节点进行递归

默认情况下,当递归DOM节点的子元素时,react会同时遍历两个子元素的列表;当产生差异时,生成一个mutation。
在子元素列表末尾更新元素时,更新开销比较小。

  1. <ul>
  2. <li>first</li>
  3. <li>second</li>
  4. </ul>
  5. <ul>
  6. <li>first</li>
  7. <li>second</li>
  8. <li>third</li>
  9. </ul>

react会先匹配两个<li>first</li>对应的树,然后匹配第二个元素<li>second</li>对应的树,最后插入第三个元素的<li>third</li>树。
如果只是简单的将新增元素插入到表头,那么更新开销会比较大。比如:

  1. <ul>
  2. <li>Duke</li>
  3. <li>Villanova</li>
  4. </ul>
  5. <ul>
  6. <li>Connecticut</li>
  7. <li>Duke</li>
  8. <li>Villanova</li>
  9. </ul>

react并不会意识到应该保留**<li>Duke</li>****<li>Villanova</li>**,而是会重建每一个子元素。这种情况会带来性能问题。

keys

为了解决上述问题,react引入了key属性。当子元素拥有key时,react使用key来匹配原有树上的子元素以及最新树上的子元素。以下示例在新增key之后,使得树的转换效率得以提高:

  1. <ul>
  2. <li key="2015">Duke</li>
  3. <li key="2016">Villanova</li>
  4. </ul>
  5. <ul>
  6. <li key="2014">Connecticut</li>
  7. <li key="2015">Duke</li>
  8. <li key="2016">Villanova</li>
  9. </ul>

现在react知道只有带着'2014'key的元素是新元素,带着'2015'以及'2016'key的元素仅仅移动了。
实际开发中如果已经有了一个唯一的ID,key可以直接从数据中提取:

  1. <li key={item.id}><{item.name}</li>

key不需要全局唯一,但在列表中需要保持唯一。
使用元素在数组中的下标作为key在元素不进行重新排序时比较合适。如果有顺序修改,diff就会变慢。
当基于下标的组件进行重新排序时,组件state可能会遇到一些问题。由于组件实例是基于它们的key来决定是否更新以及复用,如果key是一个下标,那么修改顺序时会修改当前的key,导致非受控组件的state(比如输入框)可能互相篡改,会出现无法预期的变动。

diff算法缺点

  • 该算法不会尝试匹配不同组件类型的子树。如果你发现你在两种不同类型的组件中切换,但输出非常相似的内容,建议把它们改成同一类型。
  • key应该具有稳定性,可预测,以及列表内唯一的特质。不稳定的key(比如通过Math.random()生成的)会导致许多组件实例和DOM节点被不必要地重新创建,这可能导致性能下降和子组件中的状态丢失。

    React Hook

    useEffect

  • 每次render后执行,不提供第二个依赖项参数。比如useEffect(()=>{})(componentWillUpdate)

  • 仅第一次render后执行:提供一个空数组作为依赖项。比如useEffect(()=>{},[])
  • 第一次以及依赖项发生变化后执行:提供依赖项数组。比如useEffect(()=>{},[deps])
  • 组件unmount后执行:返回一个回调函数。比如useEffect(()=>{return ()=>{}},[])