八月了,可能因为上半年之前的裁员积累,最近很多小伙伴在面试,问了不少React方向的面试题,接下来我会做个《React面试小抄》。
题目来源都是各个小伙伴的面试真题,这个小抄我会不断更新,大家拿到新题也可以同步给我。
下面的面试题我会写上题解,因为每个面试题其实都可以深挖下去,所以我会加个拓展,有基础的可以面试的时候继续说下去,没有基础的就别给自己埋坑了。
好了,上题~
基础篇
类组件与函数组件组件是怎么保存state的
如果是在组件内部定义,类组件可以定义this.state上,函数组件可以使用useState或者useReducer定义。这种适合不需要多组件共享的state。
如果是在组件外部定义,可以使用常见的状态管理库,如redux、mobx等,也可以自行定义,只要保证组件能够随之更新就行。这种适合多组件共享的state。
拓展:redux、mobx、reocil、form等原理、hooks原理等。
为什么出现hooks之后,函数组件中可以定义state,保存在了哪里
hooks出现之前,函数组件内部无法定义state,主要是因为函数组件每次更新,定义在函数体里的值都要重新初始化,没法保存。
而hooks提供的useState或者useReducer可以让函数组件在组件内定义state,每一个hook都有个对应的hook对象,这个对象上会存储状态值、reducer等值,这个hook对象又以单链表的数据结构存在fiber上,而fiber是React的虚拟DOM,存在于内存中。
拓展:分析下hook对象的具体值。
useEffect怎么模拟生命周期(区分第一次(存在依赖)挂载还是更新)
useEffect(creater, deps);
复制代码
deps数组为空,则相当于componentDidMount。deps有值,则相当于componentDidUpdate。creater的返回值在组件卸载或者更新前执行。
但是与 componentDidMount
、componentDidUpdate
不同的是,传给 useEffect
的函数会在浏览器完成布局与绘制之后,在一个延迟事件中被调用。
拓展:与useLayoutEffect的区别,以及各自的使用场景。
redux 中间件问题,如果不用middleWare 能不能实现异步
可以的。
middleware只是包装了 store 的 dispatch 方法。技术上讲,任何 middleware 能做的事情,都可能通过手动包装 dispatch 调用来实现,但是放在同一个地方统一管理会使整个项目的扩展变的容易得多。
拓展:redux-thunk、redux-promise、redux-saga等中间件的使用以及原理。
使用了thunk和不使用thunk dispatch的区别
redux store 的原版 dispatch 只能接受 plainObject 类型的参数,使用了 thunk 之后,可以处理函数类型的参数,主要用于异步。
export default function isPlainObject(obj: any): boolean {
if (typeof obj !== 'object' || obj === null) return false
let proto = obj
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(obj) === proto
}
复制代码
拓展:redux、redux-thunk 原理。
react-router history 怎么实现的
react-router 使用的 history 库,
原理源码篇
redux工作流程
- createStore 创建store 数据状态管理库
- reducer 初始化、修改状态函数,定义修改规则
- getState 获取状态值 (getter)
- dispatch 提交更新 (setter)
- subscribe 变更订阅 订阅state改变之后要做的事情,一般是组件更新
下面是一个简版的redux实现:
export default function createStore(reducer) {
// 状态值
let currentState;
// 响应函数数组
let currentListeners = [];
// 获取状态
function getState() {
return currentState;
}
// 修改状态、执行相应函数
function dispatch(action) {
currentState = reducer(currentState, action);
currentListeners.forEach(listener => listener());
return action;
}
// 订阅响应函数
function subscribe(listener) {
currentListeners.push(listener);
// 取消订阅函数
return () => {
const index = currentListeners.indexOf(listener);
currentListeners.splice(index, 1);
};
}
// 初始化状态值
dispatch({type: "KKBREDUX/OOOO"});
return {
getState,
dispatch,
subscribe
};
}
复制代码
硬核原理源码篇
fiber
hooks
项目篇
先写到这儿,改天继续~