✏️ 知识整理以及学习 - React 篇

官网:

🌟 Q&A

函数组件 与 class 组件区别

函数组件类组件
一般用于比较简单的组件定义用于复杂的组件定义
this是undefinedthis指向的是当前组件的实例对象
纯函数,它接收一个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

推荐文章:我打破了 React Hook 必须按顺序、不能在条件语句中调用的枷锁

hook 在每次渲染时的查找是根据一个“全局”的下标对链表进行查找的,如果放在条件语句中使用,有一定几率会造成拿到的状态出现错乱。

Hooks 是用链表保存状态的,每次渲染的时候,必须要保证hooks的长度和顺序是一样的,如果不一致,react无法获取正确状态,会报错。

说一下前端路由,他们的实现原理分别是什么?

hash路由

基于 location.hash 来实现,https://www.xxxxxx.com#search的 location.hash 的值为 '#search'

  • URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送。
  • hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换。
  • 通过监听hashchange事件来捕捉url的变化,来决定是否更新页面:
    1. <a href="#search">search</a> 通过 a 标签设置 href 属性
    2. 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 事件机制的不同

一文吃透 react 事件系统原理

juejin.cn/editor/draf… 为什么要自定义事件机制?

  • 抹平浏览器差异,实现更好的跨平台。
  • 避免垃圾回收,React 引入事件池,在事件池中获取或释放事件对象,避免频繁地去创建和销毁。
  • 方便事件统一管理和事务机制。

react ssr(服务端渲染)是在什么场景下做的 ?

🌟 State & 生命周期

生命周期

image.png 组件的生命周期生命周期图表

挂载 :当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:

更新 :当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:

卸载 :当组件从 DOM 中移除时会调用如下方法:

  • componentWillUnmount()

    在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 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 的函数组件来说,其更新过程大致分为以下步骤:

  1. 因为某个事件 state 发生变化。
  2. React 内部更新 state 变量。
  3. React 处理更新组件中 return 出来的 DOM 节点(进行一系列 dom diff 、调度等流程)。
  4. 将更新过后的 DOM 数据绘制到浏览器中。
  5. 用户看到新的页面。

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 有什么区别 ?

官网: React.memoReact.useMemo

  • memo 是一个高阶组件,默认情况下会对 props 进行浅比较,如果相等不会重新渲染。多数情况下我们比较的都是引用类型,浅比较就会失效,所以我们可以传入第二个参数手动控制。
  • useMemo 返回的是一个缓存值,只有依赖发生变化时才会去重新执行作为第一个参数的函数,需要记住的是,useMemo 是在 render 阶段执行的,所以不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴。

useCallback 和 useMemo 的区别,它们的实现原理是什么?

官网:useCallbackReact.useMemo

  • useCallback 可缓存函数,其实就是避免每次重新渲染后都去重新执行一个新的函数。
  • useMemo 可缓存值。

有很多时候,我们在 useEffect 中使用某个定义的外部函数,是要添加到 deps 数组中的,如果不用 useCallback 缓存,这个函数在每次重新渲染时都是一个完全新的函数,也就是引用地址发生了变化,这就会导致 useEffect 总会无意义的执行。

React.forwardRef 是什么及其作用

官方文档:React.forwardRef

一般在父组件要拿到子组件的某个实际的 DOM 元素时会用到

延伸:函数式编程

说一下你理解的函数式编程?

推荐:简明 JavaScript 函数式编程——入门篇

  • 数据不可变(无副作用): 它要求你所有的数据都是不可变的,这意味着如果你想修改一个对象,那你应该创建一个新的对象用来修改,而不是修改已有的对象。
  • 无状态: 主要是强调对于一个函数,不管你何时运行,它都应该像第一次运行一样,给定相同的输入,给出相同的输出,完全不依赖外部状态的变化。

纯函数带来的意义。

  • 便于测试和优化:这个意义在实际项目开发中意义非常大,由于纯函数对于相同的输入永远会返回相同的结果,因此我们可以轻松断言函数的执行结果,同时也可以保证函数的优化不会影响其他代码的执行。
  • 可缓存性:因为相同的输入总是可以返回相同的输出,因此,我们可以提前缓存函数的执行结果。
  • 更少的 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 包教包会(一):介绍 Redux 三大核心概念

redux里面dispatch是如何正确找到reducer的?

combineReducers源码

是的,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吗?

juejin.cn/post/705589…

🌟 React 性能优化手段

在React中,有做过什么性能优化吗?

  • React 项目中有哪些细节可以优化?
  • 实际开发中都做过哪些性能优化?

推荐文章:React 性能优化的 8 种方式了解一下?

  • 使用 React.memo 来缓存组件。
  • 使用 React.useMemo 缓存大量的计算。
  • 避免使用匿名函数。
  • 利用 React.lazyReact.Suspense 延迟加载不是立即需要的组件。
  • 尽量使用 CSS 而不是强制加载和卸载组件。
  • 使用 React.Fragment 避免添加额外的 DOM。

如果有一个非常大的reac页面,我想优先渲染某一部分,这该怎么做 ?

分类:
前端
标签: