前序
又来面试了,这次是百度的无人驾驶方向,这次是一面
过程
先自我介绍下
吃了前两次的亏,这次把先讲了学历背景,按时间线讲了公司和经历的项目的挑战和解决方案,没讲完,中间有面试官感兴趣的,他问了几个,然后让我别讲了,时间超标了。。。
首先进入框架环节,你现在主 react 是吧,那么我们先来聊聊 fiber 架构吧
- 早先的树形结构从上到下遍历渲染,不可中断,如果中间有耗时操作,那么后面的就没法渲染了
- 所以有了 fiber 架构,使用链表链接各个 fiber 节点,每个节点的任务可以中断,然后重新渲染,这样就不会阻塞后面的任务了
- 分片调度,使用 MessageChannel 加优先级队列实现调度
- 双 fiber 树,一个在 wokingInProgress,一个在 render,交替渲染,提高渲染效率
那我们继续挖深一点,这个中断是什么发生的,其中的优先级知道么
中断的发生有好几种情况
- 低优先级任务执行中有高优先级插入(比如输入事件回调)会中断低优先级的
- 任务执行时间超过 5ms,为了不影响渲染帧率,会中断任务执行,丢到后面有空闲的时候执行
- React Fiber一个更新过程被分为两个阶段(Phase):第一个阶段Reconciliation Phase和第二阶段Commit Phase。第二个commit阶段不可被中断
优先级的话一共有五个
- 1-Immediate,最高优先级,需要立即执行
- 2-UserBlocking,次高优先级,用于用户交互,用户阻塞优先级,如用户输入、点击等交互
- 3-Normal,普通优先级(默认优先级)
- 4-Low,低优先级,用于一些低优先级的任务
- 5-Idle,最低优先级,只在浏览器空闲时执行
那么它在渲染的是串行还是并行的,或者说批量更新
这里我猜应该是想问 setState 触发更新特点,我回答了看情况
react18之前
- 在 renderContext 中比如事件的回调中直接调用 setState,多个会合并成一次更新
- 不在 renderContext 中,比如 setTimeout 中调用 setState,setState 一次立马触发更新一次
react18之后 - 多个会合并成一次更新
我们继续,来聊聊 hook 吧,我有一个大的 state 在父组件,如何减少子组件的渲染次数
- 使用 useMemo,memo 控制子组件的 props 更新
- 使用 useImmer 控制对象级 state 得子属性更新导致的子组件更新
- 使用 useCallback,保证传入的回调函数不变,减少子组件更新
嗯嗯,差不多也就是这些,那你知道为啥不要在条件、循环、嵌套函数中调用 Hook 么?
- React 通过维护一个 Hook 链表来记录每个 Hook 的状态。
- 每次组件渲染时,React 会按照 Hooks 的调用顺序处理这些 Hook。
- 如果在 if 语句中使用 Hooks,可能会导致某些 Hooks 被跳过,从而打乱调用顺序,导致状态管理错误
en, 问个场景知道怎么父掉子么
useImperativeHandle + forwardRef 把方法放入子组件的 ref 中
en, 换个问题,react 合成事件了解不
当时只讲了 2 和 3、
- react 把所有事件委托到根容器上,自定义实现了事件机制,减少内存消耗和兼容不同浏览器
- react17 之前,委托到 document 上,使用事件池子复用事件,事件的捕获在原生事件的冒泡阶段后
- react17 之后,委托到当前根节点上,迎合微前端潮流,取消了事件池子,事件的捕获在原生事件的捕获阶段后
- 事件合成,其实就是把原生事件包装成合成事件,方便统一处理,比如阻止冒泡、阻止默认行为、事件代理等,原生事件的阻止是可以通过 return false, react 的合成事件,必须调用合成事件自己实现的方法,比如 stopPropagation、preventDefault
- 当事件处理结束后,会销毁合成事件上添加的属性,这也是为啥我们异步获取不到 e.target.value 的原因
en, 我们来考考写代码的能力吧,实现一个类 Chian, new Chian(data).map(x => x + 1).filter(x => x > 5).value() 1.要求实现链式调用;2 要求惰性求值,只有在调用 value 的时候才执行 map 和 filter
class Chian {
constructor(data) {
this.data = data
this.fnList = []
}
map(fn) {
this.fnList.push({
type: 'map',
fn
})
return this
}
filter(fn) {
this.fnList.push({
type: 'filter',
fn
})
return this
}
value() {
if (!this.fnList.length) return this.data
return this.fnList.reduce((pre, cur) => {
switch (cur.type) {
case 'map':
return pre.map(cur.fn)
case 'filter':
return pre.filter(cur.fn)
default:
throw new Error('type error')
}
}, this.data)
}
}
const res = new Chian([1, 2, 3, 4, 5, 6, 7, 8, 9]).map(x => x + 1).filter(x => x > 5).value()
console.log(res) // [7, 8, 9, 10, 11]
面试结束,想问啥
两个问题,问多了对方会烦的,最好是岗位相关的证明你的入职趋向性
- 我进去了要做啥(岗位相关)
- 团队分享文化怎么样(团队氛围文化相关)
总结
- 自我介绍讲太细了,没必要,当然得看情况,比如你想让面试官多问问你的项目,还是要多讲的复合star原则就行
- react fiber 架构,优先级队列,双 fiber 树,中断,批量更新,合成事件,链式调用,惰性求值,这些知识点都得知道,而且要能讲出来,不然面试官会问你原理,还是得好好补一下
- 一定要时刻保持结构化表达(金字塔原理的从上到下细分,从左到右递进),这样面试官才会觉得你是个老职场人,说的清楚事,分的清楚事和流程