官网:
🌟 Q&A
函数组件 与 class 组件区别
函数组件 | 类组件 |
---|---|
一般用于比较简单的组件定义 | 用于复杂的组件定义 |
this是undefined | this指向的是当前组件的实例对象 |
纯函数,它接收一个props对象返回一个react元素 | 继承React.Component并且创建render函数返回react元素 |
无生命周期和状态state | 有生命周期和状态state |
性能消耗小,不需要创建实例,渲染的时候就执行一下,得到返回的react元素后就直接把中间量全部都销毁 | 性能消耗比较大,因为类组件需要创建类组件的实例,而且不能销毁 |
PureComponent和Component的区别
什么场景会触发重新渲染
React合成事件是如何实现的
React dom绑定事件和原生事件有什么区别
context的实现原理是什么?如何做依赖收集?
如果在map循环中没有设置key值,那么从 A B C D 四个节点变成 B C D三个节点,它会以什么样的方式变化
HOC 和 hooks 的区别
hoc 能复用逻辑和视图,hook 只能复用逻辑
为什么不能在条件语句中写 hook
hook 在每次渲染时的查找是根据一个“全局”的下标对链表进行查找的,如果放在条件语句中使用,有一定几率会造成拿到的状态出现错乱。
Hooks 是用链表保存状态的,每次渲染的时候,必须要保证hooks的长度和顺序是一样的,如果不一致,react无法获取正确状态,会报错。
说一下前端路由,他们的实现原理分别是什么?
hash路由
基于 location.hash
来实现,https://www.xxxxxx.com#search
的 location.hash 的值为 '#search'
- URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送。
- hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换。
- 通过监听
hashchange
事件来捕捉url的变化,来决定是否更新页面:<a href="#search">search</a>
通过 a 标签设置 href 属性location.hash="#search"
js 改变 loaction.hash 值
history路由
基于 History API实现,主要的 API 有以下两个:history.pushState()
和 history.repalceState()
。这两个 API可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录。
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
复制代码
特性:
- pushState 和 repalceState 的标题(title):一般浏览器会忽略,最好传入 null ;
- 我们可以使用 popstate 事件来监听 url 的变化;
- history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面渲染;
react key
key是一个字符串,相当于组件在当前层级下的唯一id,因为同一层级下,组件类型也经常相同,那这个时候再区分节点,就需要一个唯一的key了。
key是用于判断是否可以复用新老VDOM节点的,那这个值必须得唯一且稳定的
都用过那些版本的react,分别介绍一下区别?
react hooks 与 class 组件对比
函数组件怎么实现shouldComponentUpdate?
讲一下react的事件机制,React 16 和 React 17 事件机制的不同
juejin.cn/editor/draf… 为什么要自定义事件机制?
- 抹平浏览器差异,实现更好的跨平台。
- 避免垃圾回收,React 引入事件池,在事件池中获取或释放事件对象,避免频繁地去创建和销毁。
- 方便事件统一管理和事务机制。
react ssr(服务端渲染)是在什么场景下做的 ?
🌟 State & 生命周期
生命周期
挂载 :当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
constructor()
:在constructor
中进行state
、props
的初始化,在这个阶段修改state
,不会执行更新阶段的生命周期,可以直接对state
赋值static getDerivedStateFromProps()
render()
componentDidMount()
:发生在 render 函数之后,已经挂载 Dom
更新 :当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
卸载 :当组件从 DOM 中移除时会调用如下方法:
-
在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount 中创建的订阅等。componentWillUnmount 中不应调用 setState,因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它
错误处理 :当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
废弃了哪三个生命周期,为什么?
React16新的生命周期弃用这三个钩子函数,非要使用必须加前缀:
UNSAVE_
componentWillMount
componentWillReceiveProps
componentWillUpdate
新增了
getDerivedStateFromProps
getSnapshotBeforeUpdate
React16并没有删除这三个钩子函数,但是不能和新的钩子函数混用,React17将会删除弃用的这三个函数
废弃的原因,是在React16的Fiber架构中,调和过程会多次执行will周期,不再是一次执行,失去了原有的意义。此外,多次执行,
在周期中如果有setState或dom操作,会触发多次重绘,影响性能,也会导致数据错乱
setState 相关问题
- setState是同步还是异步的,什么情况下同步,什么情况下异步?
- ❓ React setState 怎么获取到更新后的值? 异步函数中为什么setState 会立即更新?
- setState返回一样的引用,render会执行吗
setState
本身代码的执行肯定是同步的,这里的异步是指是多个 state 会合成到一起进行批量更新。 同步还是异步取决于它被调用的环境。
- 如果
setState
在 React 能够控制的范围被调用,它就是异步的。比如合成事件处理函数,生命周期函数, 此时会进行批量更新,也就是将状态合并后再进行 DOM 更新。 - 如果
setState
在原生 JavaScript 控制的范围被调用,它就是同步的。比如原生事件处理函数,定时器回调函数,Ajax 回调函数中,此时setState
被调用后会立即更新 DOM 。
🌟 React hooks
相关文章:
用动画和实战打开 React Hooks(一):useState 和 useEffect
用动画和实战打开 React Hooks(二):自定义 Hook 和 useCallback
用动画和实战打开 React Hooks(三):useReducer 和 useContext React Hooks 源码解析(4):useEffect
Hooks的实现原理?不用链表可以用其他方法实现吗?
基于链表来实现的,也可以用数组来模拟。
React Hooks有什么优势和劣势
react useEffect 对应 class 组件的哪些生命周期 ?
useEffect、useMemo、useCallback是如何做依赖收集的 ?
useEffect的使用方法?useEffect的return会在什么时候执行?useEffect原理是什么?
useEffect 和 useLayoutEffect 区别
对于 React 的函数组件来说,其更新过程大致分为以下步骤:
- 因为某个事件
state
发生变化。 - React 内部更新
state
变量。 - React 处理更新组件中 return 出来的 DOM 节点(进行一系列 dom diff 、调度等流程)。
- 将更新过后的 DOM 数据绘制到浏览器中。
- 用户看到新的页面。
useEffect
在第 4 步之后执行,且是异步的,保证了不会阻塞浏览器进程。 useLayoutEffect
在第 3 步至第 4 步之间执行,且是同步代码,所以会阻塞后面代码的执行。
useEffect
不会阻塞浏览器渲染,而useLayoutEffect
会阻塞浏览器渲染。useEffect
会在浏览器渲染结束后执行,useLayoutEffect
则是在DOM
更新完成后,浏览器绘制之前执行。
useEffect
传给 useEffect 的函数会在浏览器完成布局与绘制之后
,在一个延迟事件中被调用。这使得它适用于许多常见的副作用场景,比如设置订阅和事件处理等情况,因为绝大多数操作不应阻塞浏览器对屏幕的更新
useLayoutEffect
并非所有 effect 都可以被延迟执行。例如,一个对用户可见的 DOM 变更就必须在浏览器执行下一次绘制前被同步执行,这样用户才不会感觉到视觉上的不一致。(概念上类似于被动监听事件和主动监听事件的区别。)React 为此提供了一个额外的 useLayoutEffect
Hook 来处理这类 effect。它和 useEffect
的结构相同,区别只是调用时机不同
useLayoutEffect
可避免闪烁现象
useEffect 依赖为空数组与 componentDidMount 区别
在 render
执行之后,componentDidMount
会执行,如果在这个生命周期中再一次 setState
,会导致再次 render
,返回了新的值,浏览器只会渲染第二次 render
返回的值,这样可以避免闪屏。
但是 useEffect
是在真实的 DOM 渲染之后才会去执行,这会造成两次 render
,有可能会闪屏。
实际上 useLayoutEffect
会更接近 componentDidMount
的表现,它们都同步执行且会阻碍真实的 DOM 渲染的。
memo 和 useMemo 有什么区别 ?
memo
是一个高阶组件,默认情况下会对props
进行浅比较,如果相等不会重新渲染。多数情况下我们比较的都是引用类型,浅比较就会失效,所以我们可以传入第二个参数手动控制。useMemo
返回的是一个缓存值,只有依赖发生变化时才会去重新执行作为第一个参数的函数,需要记住的是,useMemo
是在render
阶段执行的,所以不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于useEffect
的适用范畴。
useCallback 和 useMemo 的区别,它们的实现原理是什么?
useCallback
可缓存函数,其实就是避免每次重新渲染后都去重新执行一个新的函数。useMemo
可缓存值。
有很多时候,我们在 useEffect
中使用某个定义的外部函数,是要添加到 deps
数组中的,如果不用 useCallback
缓存,这个函数在每次重新渲染时都是一个完全新的函数,也就是引用地址发生了变化,这就会导致 useEffect
总会无意义的执行。
React.forwardRef 是什么及其作用
官方文档:React.forwardRef
一般在父组件要拿到子组件的某个实际的 DOM 元素时会用到
延伸:函数式编程
说一下你理解的函数式编程?
- 数据不可变(无副作用): 它要求你所有的数据都是不可变的,这意味着如果你想修改一个对象,那你应该创建一个新的对象用来修改,而不是修改已有的对象。
- 无状态: 主要是强调对于一个函数,不管你何时运行,它都应该像第一次运行一样,给定相同的输入,给出相同的输出,完全不依赖外部状态的变化。
纯函数带来的意义。
- 便于测试和优化:这个意义在实际项目开发中意义非常大,由于纯函数对于相同的输入永远会返回相同的结果,因此我们可以轻松断言函数的执行结果,同时也可以保证函数的优化不会影响其他代码的执行。
- 可缓存性:因为相同的输入总是可以返回相同的输出,因此,我们可以提前缓存函数的执行结果。
- 更少的 Bug:使用纯函数意味着你的函数中不存在指向不明的 this,不存在对全局变量的引用,不存在对参数的修改,这些共享状态往往是绝大多数 bug 的源头。
有哪些库是利用了函数式编程的思想?
lodash是真正的函数式编程库吗?
🌟 React Virtual DOM DIFF
介绍一下 Vdom
Virtual DOM
即 虚拟DOM,是一棵以JavaScript
对象作为基础的树,在React
中通常是通过JSX
编译而成的,每一个节点称为VNode
,用对象属性来描述节点,实际上它是一层对真实DOM
的抽象,最终可以通过渲染操作使这棵树映射到真实环境上,简单来说Virtual DOM
就是一个Js
对象,用以描述整个文档
对 React Fiber 的理解
推荐文章:
React.fiber了解吗? 造成卡顿的原因是什么? react.fiber里面是怎么解决的? 中断更新之后,下次是如何找到要更新的位置的?
fiber的实现原理
fiber的时间调度通过哪两个原生api实现的(requestAnimationFrame和requestIdleCallback???)
介绍 React Vdom diff 算法
推荐文章:
React
在内存中维护一颗虚拟DOM
树,当数据发生改变时(state & props
),会自动的更新虚拟DOM
,获得一个新的虚拟DOM
树,然后通过Diff
算法,比较新旧虚拟DOM
树,找出最小的有变化的部分,将这个变化的部分Patch
加入队列,最终批量的更新这些Patch
到实际的DOM
中
🌟 React Redux
redux里面dispatch是如何正确找到reducer的?
是的,redux它不找,一把梭,每次dispatch都要遍历一遍所有的reducer...
redux怎么挂载中间件的?它的执行顺序是什么样的?
核心函数compose
function compose(middlewears) {
return middlewears.reduce((a, b) => (...argu) => a(b(...argu)))
}
复制代码
执行顺序:从后往前执行redux中间件。
使用Redux时需要注意的点
如果Redux没返回新的数据会怎样
Redux是如何派发数据的? connect原理?
redux的缺点?
除了redux,还用过其他的状态管理库吗?
你能手写个简单的redux吗?
🌟 React 性能优化手段
在React中,有做过什么性能优化吗?
- React 项目中有哪些细节可以优化?
- 实际开发中都做过哪些性能优化?
- 使用
React.memo
来缓存组件。 - 使用
React.useMemo
缓存大量的计算。 - 避免使用匿名函数。
- 利用
React.lazy
和React.Suspense
延迟加载不是立即需要的组件。 - 尽量使用 CSS 而不是强制加载和卸载组件。
- 使用
React.Fragment
避免添加额外的 DOM。