React面试题整理[入门版]

7,221 阅读11分钟

这是我自己整理的答案,请各位大佬不吝赐教,错的帮我指出来,还有哪些热门考点(不局限与react)也发我一份~多谢

面试题答案

  1. React

    1. 介绍Redux,主要解决什么问题?数据流程是怎么样的?多个组件使用相同状态如何进行管理?

      1. Redux是一个状态容器,解决了组件间的状态共享问题,实现了多组件通信,也是一种MVC机制
      2. Redux4个模块
        1. view - 组件状态改变时,dispatch一个action
        2. action:生成一个描述状态变化的action对象,并传递给store
        3. store:接收action对象并传递给reducer,驱动reducer执行状态更新任务,更新后刷新dom节点(即view中执行render)
        4. reducer:对action对象处理,更新组件状态,并将新的状态值返回store
      3. Redux数据流程:
        1. 创建一个store存储数据,定义改变store的action,里面是各种reducer的映射
        2. view层dispatch一个action,store接收到action修改状态,执行update方法,调用subscribe传进来的回调
        3. view层在componentDidMount中订阅状态更新,拿到更新后的store后setState、forceUpdate更新view
      4. 多组件使用相同状态。都放到同一个reducer里管理即可。
    2. React-Redux到react组件的连接过程

      1. Provider为后代提供store:使用时在应用的最外层包一层,本质是上下文跨层级传递store
      2. connect为组件提供数据和变更方法:本质是高阶组件,连接React组件与Redux store,返回一个新的已经与Redux Store连接的组件类,实现多个组件使用相同状态。通过mapStateToProps和mapDispatchToProps给组件添加使用state和dispatch的方法。connect中通过setState触发组件render。
        1. mapStateToProps(Function):当Redux store发生变化时,调用该回调,此方法必须返回一个Object,这个Object与组件的props融合,不传mapStateToProps时组件不监听Redux store。谨慎使用第二个参数ownProps,父组件的prop改变也会引起mapStateToProps执行
        2. mapDispatchToProps:返回一个对象(action creator映射)或function接收一个dispatch,可能会使用到bindActionCreator
    3. 中间件是怎么拿到store和action,然后怎么处理

      1. createStore时通过applyMiddleware函数把中间件和store关联起来的,store是在store初始化时传入中间件的。
      2. 中间件可以传入的方法参数转成数组,第一个中间件是第一次dispatch(action)时拿到action,后续中间件拿到的是经过前面的中间件处理后触发dispatch传入的action,即reduce过程
    4. Redux中间件是什么东西,接受几个参数

      1. redux中间件是一个函数,对dispatch方法增强,使dispatch不再接收纯对象的action,而是接受一个函数,把原来的dispatch当参数传进去。

      2. 接收参数是一系列的中间件函数,使用reduce实现compose,在action发出和执行reducer之间做这些操作。

      3. 手写compose:

        function compose (...funcs) {

        ​ funcs.reduce((a, b) => {

        ​ return (...args) => a(b(...args)) // ...args是第一次传入的参数

        ​ })

        }

      4. compose返回增强型的dispatch

    5. redux请求中间件如何处理并发

      1. redux-trunk中间件 - 传入dispatch的action可以定义成方法fun,fun可以拿到dispatch控制权,这样就可以自己控制何时出发reducer改变state
        1. 通过Promise.all实现并发
        2. 使用async、await配合Array.map() // forEach 循环是顺序执行不是并发?存疑
        3. 可以使用队列控制并发数量
      2. redux-saga中间件 - generator + promise
    6. Redux中异步的请求怎么处理

      1. 需要dispatch两次action,用户触发第一次dispatch时执行异步操作,异步操作有返回时触发真正的dispatch
      2. 使用redux-trunk,拿到dispatch控制权,就可以自己决定何时出发reducer改变state
    7. Redux状态管理器和变量挂载到window中有什么区别

      1. 数据可控
      2. 数据响应

    8. 如何配置React-Router

      1. 浏览器应用引入react-router-dom包,会自动引入react-router,RN项目引入react-router-native
      2. 基本组件
        1. Router 路由器
        2. Link 连接
        3. Route 路由
        4. Switch 独占
        5. Redirect 重定向
      3. Route渲染的3种方式,按优先级排列
        1. children 不匹配时match为null
        2. component
        3. render
      4. 注意事项:使用内联函数渲染时,需要使用render/children,而不是component
      5. 动态路由
      6. 嵌套路由
      7. 路由守卫:创建高阶组件包装Route使其具有权限判断功能,一般结合store
    9. react-router怎么实现路由切换?BrowserRouter as Router

      1. Router组件使用Provider向下提供history对象,以及location的变更监听
      2. Route接收loaction(props || context),Router尝试其属性path与location进行匹配,匹配检测(children > component > render),内容渲染
      3. Link 跳转链接,生成一个a标签,禁用默认事件,js处理点击事件跳转(history.push) -> Link与直接使用a标签的区别是a页面会重新加载一下,Link只重新渲染Route组件部分
      4. Redirect to
      5. 从上下文中获取history this.context.router.history.push...
    10. 路由的动态加载模块

      1. component:import(...),打包时按import进来的路由页面分割成多个chunk,按需加载
      2. 使用react-loadable包实现懒加载
    11. 前端怎么控制管理路由

      1. 实现一个PrivateRoute高阶组件做路由守卫
    12. 使用路由时出现问题如何解决

      版本升级带来的问题


    13. 多个组件之间如何拆分各自的state,每块小的组件有自己的状态,它们之间还有一些公共的状态需要维护,如何思考这块

      1. 每块小组件自己的状态放到私有state中,公共的才用Redux管理
    14. React生命周期及自己的理解,以及V16对生命周期的修改

      1. 3阶段
        1. 挂载时
          1. constructor
          2. getDerivedStateFromProps
          3. render -> React更新DOM和refs
          4. componentDidMount (commit阶段)
        2. 更新时 -> 触发更新的条件:父props改变、setState、forceUpdate()
          1. getDerivedStateFromProps
          2. shouldComponentUpdate -> 可优化组件渲染
          3. render
          4. getSnapshotBeforeUpdate (在commit前夕执行) -> React更新DOM和refs
          5. componentDidUpdate
        3. 卸载时
          1. componentWillUnmount
      2. 生命周期的变革?原因?
        1. v17打算删除3个will,fiber出现后,任务切片可以被中断,这3个will方法可能会多次执行,不符合生命周期定义初衷
          1. componentWillMount
          2. componentWillReceiveProps
          3. componentWillUpdate
        2. 新引入2个:
          1. static getDerivedStateFromProps -> 不能获取this,不在那些will里引起副作用
          2. getSnapshotBeforeUpdate:使用场景不多,仅对老api的替代
    15. 介绍react优化

      1. 核心:只渲染需要渲染的,只计算需要计算的
      2. state扁平化
      3. 是否需要重新渲染?
        1. class组件:shouleComponentUpdate、PureComponent(state的浅比较)
        2. function组件:使用React.memo高阶函数,第二个参数类似shouldComponentUpdate
        3. ReactHooks:useMemo(缓存耗能操作的返回结果)(userMemo要不别说了)
        4. useMemo与useEffect的差别:useEffect在dom更新后执行,useMemo在渲染期执行
    16. React组件事件代理的原理

      1. 事件委托:事件注册到document上
        1. 统一管理卸载
        2. 避免同一类型事件注册多次,如
      2. react源码通过一个合成事件映射表判断是否是合成事件
      3. e.stopPropagation阻止冒泡不可达到document层,解决:e.nativeEvent.stopImmediatePropagation()
    17. diff、patch、update与vue的对比

      1. react中某个组件状态改变,react会把此组件以及后代全部重新渲染,但并不意味着渲染会丢弃上一次的渲染结果,react会diff新老vnode,再patch到dom上。
      2. 组件树过于庞大的话diff算法会损耗一些性能,需要开发者设置shouldComponentUpdate钩子,V16引入fiber
      3. 与Vue的对比:vue在get时实现了依赖收集,不会比较整棵树,更加细粒度的去更新状态有变化的组件,同时defineProperty也不存在像PureComponent对象浅比较的情况
      4. react中state要设计扁平,方便优化shouldComponentUpdate
    18. React Render Props模式 - 渲染劫持

      1. 形式:
        1. props: A组件上传递一个返回B组件的方法fun,fun的形参将来A组件调用fun渲染B时传入,形参数来自A组件
        2. Chidden: 含义一样,props变成了B的子节点,类似吧?
      2. 目的:渲染B组件时,参数来自A
      3. 用处:逻辑复用,把纯逻辑抽取出来封装一个组件(相当于上面的A组件),比如表单的通用处理onChange方法等
      4. vue中类似逻辑复用:directive
    19. import { Button } from 'antd',打包的时候只打包button,分模块加载,是怎么做到的

      1. 通过 babel-plugin-import 配置处理。

        {
          "plugins": [
            ["import", {
              "libraryName": "antd",
              "libraryDirectory": "es",
              "style": "css"
            }]
          ]
        }
        
      2. 相当于

        import Button from 'antd/es/button';
        import 'antd/es/button/style/css';
        
    20. React挂载的时候有3个组件,textComponent、composeComponent、domComponent,区别和关系

      不太清楚这个考点是什么,React.createElement第一个参数的type吗?只知道class、function的区别,前者调用实例的render方法,后者直接执行

    21. React中Dom结构发生变化后内部经历了哪些变化?不理解问题的本质

      1. setState引起状态变化
      2. React重新构建vnode树
      3. 和旧vnode树对比,得出变化部分Patches(diff算法)
      4. 将Patches更新到真实dom上
    22. setState是同步还是异步

      1. 结论:原生事件、setTimeout中是同步,合成事件、生命周期中是异步
      2. 原因:React批量更新
        1. 源码中根据isBatchingUpdates(默认false,同步更新)判断立即更新,还是放到队列中延迟更新
        2. batchedUpdates方法会修改isBatchingUpdates为true,引起setState的异步更新
        3. React调用事件处理函数前会先调用batchedUpdates方法,所以由React处理的事件setState为异步。
    23. React怎么做数据的检查和变化

      1. 批量更新 - 见setState同步、异步问题
      2. 下一个宏任务执行前批量更新dom -> Promise.then
    24. 哪些方法会触发react重新渲染

      1. setState
      2. 父组件render
      3. forceUpdate
    25. React15/16.x的区别

      1. reconciler过程采用任务切片
      2. 生命周期的修改
    26. react常见的通信方式

      1. props
      2. Context - 高级API不要乱用
        1. React-Redux:传递store
        2. React-router:传递history、location
        3. 子组件获取context的3种方式:
          1. class static contextType
          2. hooks:useState
      3. 多个context
        1. class只能接受单一context
        2. consumer可以层层嵌套,会被打死
        3. useState可以接收多个state
      4. 注意:
        1. Provider的value是一个对象的话要挂在父元素的state,因为Provider的props对象改变的话会引起子组件渲染
      5. 使用redux
    27. 介绍React高阶组件

      1. 接收一个组件返回一个组件
      2. React Render Props模式(上面有)
    28. diff算法

      1. diff算法策略

        1. 不采用优化算法,把一个树转成另一个树的最小操作时间复杂度O(n3)

        2. 同级比较:Web中跨层移动节点几乎没有

        3. 不同类型(type、key)两个组件生成不同的树形结构

        4. 通过key暗示哪些元素相同

      2. diff过程 - 比对两个vnode时有3种操作:删除、替换、更新

        1. 删除:newVnode不存在
        2. 替换:vnode和newNode的type或key不同
        3. 更新:有相同类型的key但vnode和newVnode不同
      3. 当比较同类型元素时,React更新属性后执行render并将新老vdom尽心递归对比。

      4. 与vue的区别参考第17题

    29. Fiber架构

      1. react使用fiber数据机构存放组件树的附加信息
      2. 第一次渲染执行render、后面先diff再渲染
      3. fiber渲染树的深度优先遍历:下一个fiber优先级:child > sibling > parent.sibling
      4. reconcilerChildren中根据children创建fiber渲染树,并进行diff,这里的diff也是和vue diff算法不同的地方,vue是靠一些假设两端向中间夹逼。
      5. 画fiber渲染树 按照优先级顺序,一层一层的形成链表结构,rootFiber在render里创建。
      6. fiber都执行完毕后commit,commit不能被打断,一次性更新完dom,找到父节点,根据fiber对象上的effectTag做对应的dom操作
      7. fiber优先级:计算过期时间
      8. fiber数据结构:
        1. type:类型,区分不同的fiber,比如说function class host等
        2. props:属性参数等
        3. node:真实dom节点 - 新增时是null
        4. base:存储旧fiber,用于diff - 新增时是null
        5. parent:workInProgressFiber,父节点
      9. fiber流程简单版总结:
        1. render中初始化子任务
        2. window.requestIdleCallback 执行performUnitOfWork
          1. 更新当前fiber - 里面会调用reconcilerChildren,给每个fiber打上effectTag
          2. 返回下一个fiber
        3. fiber渲染树上的任务执行完成后commit,根据effectTag更新dom
    30. why use hook?

      1. hook之前的函数组件时无状态、无副作用,只作单纯的展示组件
      2. class组件的弊端,为什么要引入hook?
        1. 组件之间复用逻辑难,比如使用Context要引入Provider、Consumer,一层套一层
        2. 复杂组件变得难以理解:hook将组件中相互关联的部分拆成更小的函数
        3. 难以理解的class:比如js中的this问题、构建时的一些问题
      3. 引入hook之后函数组件发生了哪些变化?
        1. 函数组件可以存储和改变状态值:useState、useReducer
        2. 可以执行副作用:useEffect
        3. 复用状态逻辑: 自定义hook