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_nctrackfunction tree(props){
return DOMLElement
}
function tree(props){
return React.createElement(props.name, props.props, props.children.map( child => tree()))
}
[ ] 小米|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函数
function render( vnode, container ) {
// 当vnode为字符串时,渲染结果是一段文本
if ( typeof vnode === 'string' ) {
const textNode = document.createTextNode( vnode );
return container.appendChild( textNode );
}
const dom = document.createElement( vnode.tag );
if ( vnode.attrs ) {
Object.keys( vnode.attrs ).forEach( key => {
if ( key === 'className' ) key = 'class'; // 当属性名为className时,改回class
dom.setAttribute( key, vnode.attrs[ key ] )
} );
}
vnode.children.forEach( child => render( child, dom ) ); // 递归渲染子节点
return container.appendChild( dom ); // 将渲染结果挂载到真正的DOM上
}
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的构成
- 转转|讲一下
redux
,redux
数据是怎么渲染到组件的 - 绿盟|redux原理
- 字节|redux工作流程
- 阿里云|react hooks 在使用时有哪些常见的坑
- 字节|React useCallback 什么作用(memorize函数,重新渲染时不会重新定义,但会重新运行。我之前的理解是错的,以为是 useMemo 的函数版)
- 字节|react hooks
[ ] 字节|看结果
function Index(){
const [ num ,setNumber ] = React.useState(0)
const handerClick=()=>{
for(let i=0; i<5;i++ ){
setTimeout(() => {
setNumber(num+1)
console.log(num)
}, 1000)
}
}
return <button onClick={ handerClick } >{ num }</button>
}
[ ] 字节|useMemo和useCallback的区别,以及用法
[ ] 字节|问一个
react hook
题目, 假如有一个这样的组件:
现已知上述代码不安全, 如果第 5 行执行时间过长, 在第 5 行阻塞时, 页面被用户手动关闭了, 再执行第 6 行时会报 warning, 请问如何改造代码, 使setCount
变得安全?
答: 用 useRef 记录组件的生存状态, 在 useEffect 当中写 return 函数, 组件卸载时将 useRef 记录的状态转为 falseconst Demo: FC = () => {
const [count, setCount] = useState(0);
// 组件初始化时执行该函数, 向后端请求 count 的初始值
const init = async () => {
const value = await getCount(); // 向后端发出请求获取初始数据
setCount(value); // 不安全
}
useEffect(() => {
init();
});
// return ...;
}
[ ] 北森|对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
推荐使用方法:在调用setState时使用函数传递state值,在回调函数中获取最新更新后的state。
4.1.2 合成事件
4.2 Reconciliation(协调)
Diffing算法
对比两棵树时,react首先比较两棵树的根节点。不同类型的根节点元素会有不同的形态。
对比不同类型的元素(根节点)
当根节点为不同类型的元素时,react会拆卸原有的树并且建立起新的树。(当一个元素从变成会触发一个完整的重建流程)。
当卸载一棵树时,对应的DOM节点也会被销毁。组件实例将执行componentWillUnmount()
方法。当建立一棵新的树时,对应的DOM节点会被创建以及插入到DOM中。组件实例将执行UNSAFE_componentWillMount()
方法,紧接着componentDidMount()
方法。所有与之前的树相关的state也会被销毁。
- <div>
<Counter/>
- </div>
+ <span>
<Counter/>
+ </span>
当对比以上变更时,react会销毁Counter
组件并且重新装载一个新的组件。
对比同一类型的元素
当对比两个相同类型的react元素时,react会保留DOM节点,仅比对及更新有改变的属性。
当更新style
属性时,react仅更新有所变更的属性。
- <div style={{color: 'red', fontWeight: 'bold'}} />
+ <div style={{color: 'green', fontWeight: 'bold'}} />
通过对比上述两个元素,react知道只需要修改DOM元素上的color
样式,无需修改fontWeight
。
在处理完当前节点之后,react继续对子节点进行递归。
对比同类型的组件元素
当一个组件更新时,组件实例会保持不变,因此可以在不同的渲染时保持state一致。react将更新该组件实例的props以保证与最新的元素保持一致,并且调用该实例的UNSATF_componentWillReceiveProps()
、UNSAFE_componentWillUpdate()
以及componentDidUpdate()
方法。
下一步,调用render()方法,diff算法将在之前的结果以及新的结果中进行递归。
对子节点进行递归
默认情况下,当递归DOM节点的子元素时,react会同时遍历两个子元素的列表;当产生差异时,生成一个mutation。
在子元素列表末尾更新元素时,更新开销比较小。
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
react会先匹配两个<li>first</li>
对应的树,然后匹配第二个元素<li>second</li>
对应的树,最后插入第三个元素的<li>third</li>
树。
如果只是简单的将新增元素插入到表头,那么更新开销会比较大。比如:
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>
<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
react并不会意识到应该保留**<li>Duke</li>**
和**<li>Villanova</li>**
,而是会重建每一个子元素。这种情况会带来性能问题。
keys
为了解决上述问题,react引入了key
属性。当子元素拥有key
时,react使用key
来匹配原有树上的子元素以及最新树上的子元素。以下示例在新增key
之后,使得树的转换效率得以提高:
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
现在react知道只有带着'2014'
key的元素是新元素,带着'2015'
以及'2016'
key的元素仅仅移动了。
实际开发中如果已经有了一个唯一的ID,key可以直接从数据中提取:
<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 ()=>{}},[])