这是我自己整理的答案,请各位大佬不吝赐教,错的帮我指出来,还有哪些热门考点(不局限与react)也发我一份~多谢
面试题答案
-
React
-
介绍Redux,主要解决什么问题?数据流程是怎么样的?多个组件使用相同状态如何进行管理?
- Redux是一个状态容器,解决了组件间的状态共享问题,实现了多组件通信,也是一种MVC机制
- Redux4个模块
- view - 组件状态改变时,dispatch一个action
- action:生成一个描述状态变化的action对象,并传递给store
- store:接收action对象并传递给reducer,驱动reducer执行状态更新任务,更新后刷新dom节点(即view中执行render)
- reducer:对action对象处理,更新组件状态,并将新的状态值返回store
- Redux数据流程:
- 创建一个store存储数据,定义改变store的action,里面是各种reducer的映射
- view层dispatch一个action,store接收到action修改状态,执行update方法,调用subscribe传进来的回调
- view层在componentDidMount中订阅状态更新,拿到更新后的store后setState、forceUpdate更新view
- 多组件使用相同状态。都放到同一个reducer里管理即可。
-
React-Redux到react组件的连接过程
- Provider为后代提供store:使用时在应用的最外层包一层,本质是上下文跨层级传递store
- connect为组件提供数据和变更方法:本质是高阶组件,连接React组件与Redux store,返回一个新的已经与Redux Store连接的组件类,实现多个组件使用相同状态。通过mapStateToProps和mapDispatchToProps给组件添加使用state和dispatch的方法。connect中通过setState触发组件render。
- mapStateToProps(Function):当Redux store发生变化时,调用该回调,此方法必须返回一个Object,这个Object与组件的props融合,不传mapStateToProps时组件不监听Redux store。谨慎使用第二个参数ownProps,父组件的prop改变也会引起mapStateToProps执行
- mapDispatchToProps:返回一个对象(action creator映射)或function接收一个dispatch,可能会使用到bindActionCreator
-
中间件是怎么拿到store和action,然后怎么处理
- createStore时通过applyMiddleware函数把中间件和store关联起来的,store是在store初始化时传入中间件的。
- 中间件可以传入的方法参数转成数组,第一个中间件是第一次dispatch(action)时拿到action,后续中间件拿到的是经过前面的中间件处理后触发dispatch传入的action,即reduce过程
-
Redux中间件是什么东西,接受几个参数
-
redux中间件是一个函数,对dispatch方法增强,使dispatch不再接收纯对象的action,而是接受一个函数,把原来的dispatch当参数传进去。
-
接收参数是一系列的中间件函数,使用reduce实现compose,在action发出和执行reducer之间做这些操作。
-
手写compose:
function compose (...funcs) {
funcs.reduce((a, b) => {
return (...args) => a(b(...args)) // ...args是第一次传入的参数
})
}
-
compose返回增强型的dispatch
-
-
redux请求中间件如何处理并发
- redux-trunk中间件 - 传入dispatch的action可以定义成方法fun,fun可以拿到dispatch控制权,这样就可以自己控制何时出发reducer改变state
- 通过Promise.all实现并发
- 使用async、await配合Array.map() // forEach 循环是顺序执行不是并发?存疑
- 可以使用队列控制并发数量
- redux-saga中间件 - generator + promise
- redux-trunk中间件 - 传入dispatch的action可以定义成方法fun,fun可以拿到dispatch控制权,这样就可以自己控制何时出发reducer改变state
-
Redux中异步的请求怎么处理
- 需要dispatch两次action,用户触发第一次dispatch时执行异步操作,异步操作有返回时触发真正的dispatch
- 使用redux-trunk,拿到dispatch控制权,就可以自己决定何时出发reducer改变state
-
Redux状态管理器和变量挂载到window中有什么区别
- 数据可控
- 数据响应
-
如何配置React-Router
- 浏览器应用引入react-router-dom包,会自动引入react-router,RN项目引入react-router-native
- 基本组件
- Router 路由器
- Link 连接
- Route 路由
- Switch 独占
- Redirect 重定向
- Route渲染的3种方式,按优先级排列
- children 不匹配时match为null
- component
- render
- 注意事项:使用内联函数渲染时,需要使用render/children,而不是component
- 动态路由
- 嵌套路由
- 路由守卫:创建高阶组件包装Route使其具有权限判断功能,一般结合store
-
react-router怎么实现路由切换?BrowserRouter as Router
- Router组件使用Provider向下提供history对象,以及location的变更监听
- Route接收loaction(props || context),Router尝试其属性path与location进行匹配,匹配检测(children > component > render),内容渲染
- Link 跳转链接,生成一个a标签,禁用默认事件,js处理点击事件跳转(history.push) -> Link与直接使用a标签的区别是a页面会重新加载一下,Link只重新渲染Route组件部分
- Redirect to
- 从上下文中获取history this.context.router.history.push...
-
路由的动态加载模块
- component:import(...),打包时按import进来的路由页面分割成多个chunk,按需加载
- 使用react-loadable包实现懒加载
-
前端怎么控制管理路由
- 实现一个PrivateRoute高阶组件做路由守卫
-
使用路由时出现问题如何解决
版本升级带来的问题
-
多个组件之间如何拆分各自的state,每块小的组件有自己的状态,它们之间还有一些公共的状态需要维护,如何思考这块
- 每块小组件自己的状态放到私有state中,公共的才用Redux管理
-
React生命周期及自己的理解,以及V16对生命周期的修改
- 3阶段
- 挂载时
- constructor
- getDerivedStateFromProps
- render -> React更新DOM和refs
- componentDidMount (commit阶段)
- 更新时 -> 触发更新的条件:父props改变、setState、forceUpdate()
- getDerivedStateFromProps
- shouldComponentUpdate -> 可优化组件渲染
- render
- getSnapshotBeforeUpdate (在commit前夕执行) -> React更新DOM和refs
- componentDidUpdate
- 卸载时
- componentWillUnmount
- 挂载时
- 生命周期的变革?原因?
- v17打算删除3个will,fiber出现后,任务切片可以被中断,这3个will方法可能会多次执行,不符合生命周期定义初衷
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
- 新引入2个:
- static getDerivedStateFromProps -> 不能获取this,不在那些will里引起副作用
- getSnapshotBeforeUpdate:使用场景不多,仅对老api的替代
- v17打算删除3个will,fiber出现后,任务切片可以被中断,这3个will方法可能会多次执行,不符合生命周期定义初衷
- 3阶段
-
介绍react优化
- 核心:只渲染需要渲染的,只计算需要计算的
- state扁平化
- 是否需要重新渲染?
- class组件:shouleComponentUpdate、PureComponent(state的浅比较)
- function组件:使用React.memo高阶函数,第二个参数类似shouldComponentUpdate
- ReactHooks:useMemo(缓存耗能操作的返回结果)(userMemo要不别说了)
- useMemo与useEffect的差别:useEffect在dom更新后执行,useMemo在渲染期执行
-
React组件事件代理的原理
- 事件委托:事件注册到document上
- 统一管理卸载
- 避免同一类型事件注册多次,如
- react源码通过一个合成事件映射表判断是否是合成事件
- e.stopPropagation阻止冒泡不可达到document层,解决:e.nativeEvent.stopImmediatePropagation()
- 事件委托:事件注册到document上
-
diff、patch、update与vue的对比
- react中某个组件状态改变,react会把此组件以及后代全部重新渲染,但并不意味着渲染会丢弃上一次的渲染结果,react会diff新老vnode,再patch到dom上。
- 组件树过于庞大的话diff算法会损耗一些性能,需要开发者设置shouldComponentUpdate钩子,V16引入fiber
- 与Vue的对比:vue在get时实现了依赖收集,不会比较整棵树,更加细粒度的去更新状态有变化的组件,同时defineProperty也不存在像PureComponent对象浅比较的情况
- react中state要设计扁平,方便优化shouldComponentUpdate
-
React Render Props模式 - 渲染劫持
- 形式:
- props: A组件上传递一个返回B组件的方法fun,fun的形参将来A组件调用fun渲染B时传入,形参数来自A组件
- Chidden: 含义一样,props变成了B的子节点,类似吧?
- 目的:渲染B组件时,参数来自A
- 用处:逻辑复用,把纯逻辑抽取出来封装一个组件(相当于上面的A组件),比如表单的通用处理onChange方法等
- vue中类似逻辑复用:directive
- 形式:
-
import { Button } from 'antd',打包的时候只打包button,分模块加载,是怎么做到的
-
通过
babel-plugin-import
配置处理。{ "plugins": [ ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }] ] }
-
相当于
import Button from 'antd/es/button'; import 'antd/es/button/style/css';
-
-
React挂载的时候有3个组件,textComponent、composeComponent、domComponent,区别和关系
不太清楚这个考点是什么,React.createElement第一个参数的type吗?只知道class、function的区别,前者调用实例的render方法,后者直接执行
-
React中Dom结构发生变化后内部经历了哪些变化?不理解问题的本质
- setState引起状态变化
- React重新构建vnode树
- 和旧vnode树对比,得出变化部分Patches(diff算法)
- 将Patches更新到真实dom上
-
setState是同步还是异步
- 结论:原生事件、setTimeout中是同步,合成事件、生命周期中是异步
- 原因:React批量更新
- 源码中根据isBatchingUpdates(默认false,同步更新)判断立即更新,还是放到队列中延迟更新
- batchedUpdates方法会修改isBatchingUpdates为true,引起setState的异步更新
- React调用事件处理函数前会先调用batchedUpdates方法,所以由React处理的事件setState为异步。
-
React怎么做数据的检查和变化
- 批量更新 - 见setState同步、异步问题
- 下一个宏任务执行前批量更新dom -> Promise.then
-
哪些方法会触发react重新渲染
- setState
- 父组件render
- forceUpdate
-
React15/16.x的区别
- reconciler过程采用任务切片
- 生命周期的修改
-
react常见的通信方式
- props
- Context - 高级API不要乱用
- React-Redux:传递store
- React-router:传递history、location
- 子组件获取context的3种方式:
- class static contextType
- hooks:useState
- 多个context
- class只能接受单一context
- consumer可以层层嵌套,会被打死
- useState可以接收多个state
- 注意:
- Provider的value是一个对象的话要挂在父元素的state,因为Provider的props对象改变的话会引起子组件渲染
- 使用redux
-
介绍React高阶组件
- 接收一个组件返回一个组件
- React Render Props模式(上面有)
-
diff算法
-
diff算法策略
-
不采用优化算法,把一个树转成另一个树的最小操作时间复杂度O(n3)
-
同级比较:Web中跨层移动节点几乎没有
-
不同类型(type、key)两个组件生成不同的树形结构
-
通过key暗示哪些元素相同
-
-
diff过程 - 比对两个vnode时有3种操作:删除、替换、更新
- 删除:newVnode不存在
- 替换:vnode和newNode的type或key不同
- 更新:有相同类型的key但vnode和newVnode不同
-
当比较同类型元素时,React更新属性后执行render并将新老vdom尽心递归对比。
-
与vue的区别参考第17题
-
-
Fiber架构
- react使用fiber数据机构存放组件树的附加信息
- 第一次渲染执行render、后面先diff再渲染
- fiber渲染树的深度优先遍历:下一个fiber优先级:child > sibling > parent.sibling
- reconcilerChildren中根据children创建fiber渲染树,并进行diff,这里的diff也是和vue diff算法不同的地方,vue是靠一些假设两端向中间夹逼。
- 画fiber渲染树 按照优先级顺序,一层一层的形成链表结构,rootFiber在render里创建。
- fiber都执行完毕后commit,commit不能被打断,一次性更新完dom,找到父节点,根据fiber对象上的effectTag做对应的dom操作
- fiber优先级:计算过期时间
- fiber数据结构:
- type:类型,区分不同的fiber,比如说function class host等
- props:属性参数等
- node:真实dom节点 - 新增时是null
- base:存储旧fiber,用于diff - 新增时是null
- parent:workInProgressFiber,父节点
- fiber流程简单版总结:
- render中初始化子任务
- window.requestIdleCallback 执行performUnitOfWork
- 更新当前fiber - 里面会调用reconcilerChildren,给每个fiber打上effectTag
- 返回下一个fiber
- fiber渲染树上的任务执行完成后commit,根据effectTag更新dom
-
why use hook?
- hook之前的函数组件时无状态、无副作用,只作单纯的展示组件
- class组件的弊端,为什么要引入hook?
- 组件之间复用逻辑难,比如使用Context要引入Provider、Consumer,一层套一层
- 复杂组件变得难以理解:hook将组件中相互关联的部分拆成更小的函数
- 难以理解的class:比如js中的this问题、构建时的一些问题
- 引入hook之后函数组件发生了哪些变化?
- 函数组件可以存储和改变状态值:useState、useReducer
- 可以执行副作用:useEffect
- 复用状态逻辑: 自定义hook
-