Skip to content
大纲

React

React 与 Vue 的区别 与 优劣

  • 模板语法不同:Vue 使用基于 HTML 的模板语法,而 React 使用 JSX 语法,它允许开发者在 JavaScript 中编写类似于 HTML 的代码。
  • 状态管理方式不同:Vue 提供了 Vuex 状态管理库,而 React 则提供了多个状态管理库,如 Redux、MobX 等。React 的状态管理更加灵活,可以根据项目的需要选择不同的状态管理库。
  • 组件间通信方式不同:Vue 使用 props事件来进行父子组件之间的通信,而 React 则使用 props回调函数进行组件之间的通信。
  • 数据绑定方式不同:Vue 使用双向数据绑定,即数据的变化将自动更新视图,而 React 则使用单向数据流,即数据只能从父组件向子组件传递
  • 渲染方式不同:Vue 使用模板渲染,在模板中定义组件和数据绑定,即将模板转化为渲染函数,再进行渲染。而 React 则使用 JavaScript 渲染,将组件和数据绑定封装成一个 JavaScript 函数,通过调用该函数来渲染组件。
  • 更新策略不同:Vue 的虚拟 DOM 是基于 Snabbdom 实现的,Vue 的虚拟 DOM 在进行更新时,会将整个组件的虚拟 DOM 树进行比较,从而找出需要更新的部分,并将这些部分进行重新渲染。而 React Fiber 则采用了增量更新的策略,它可以在每次更新时,根据优先级和时间片划分等因素,决定哪些任务需要先执行,哪些任务可以暂缓执行,从而提高了性能。
  • 对异步更新的支持不同:Vue 的虚拟 DOM 支持异步更新,但是它并没有像 React Fiber 那样针对异步更新进行优化。而 React Fiber 则专门针对异步更新进行了优化,通过时间片划分、任务优先级等机制,可以将更新任务分成多个小块,从而避免了长时间的阻塞,提高了用户体验。

React Fiber 简介

  • React Fiber 节点:是 React 中用于实现增量更新和异步渲染的一种数据结构,它包含了组件的状态、属性、子节点等信息,同时还包含了与渲染有关的信息,如渲染优先级、更新状态等。在 React Fiber 中,每个组件都对应一个 Fiber 节点。
  • Fiber 树:所有的 Fiber 节点按照组件树的结构,形成了一个 Fiber 树。它是一个树状结构,每个节点都有指向其子节点、兄弟节点和父节点的指针。
  • 工作单元:React Fiber 中的工作单元是一个包含任务的单元,它可以是一个组件的更新、一个新的组件的挂载、一个组件的卸载等。每个工作单元都有一个对应的 Fiber 节点。
  • 优先级:React Fiber 中的优先级用于决定哪些任务需要先执行,哪些任务可以暂缓执行。优先级分为以下几个等级:同步、批量更新、动画、事件等。
  • 时间切片:React Fiber 使用时间切片来划分任务的执行时间,每个时间片都有固定的时间长度,当一个任务执行的时间超过了时间片的长度时,就会被中断,并将剩余的任务留到下一个时间片中执行。(为了解决 CPU 的瓶颈与 IO 的瓶颈。实现上,则需要将同步的更新变为可中断的异步更新。
  • State Hook 节点:State Hook 用于在函数组件中添加局部状态。它返回一个状态变量和一个更新函数,通过调用更新函数,可以更新状态变量。每个 State Hook 节点都有一个唯一的 ID,它用于标识状态变量和更新函数。
  • Effect Hook 节点:Effect Hook 用于在函数组件中添加副作用。它可以在组件挂载更新卸载时执行一些副作用操作,如访问 DOM、发送网络请求等。每个 Effect Hook 节点都有一个依赖数组,它用于指定副作用的触发条件。

在 React Fiber 中,每个组件都对应一个 Fiber 节点,这个节点中包含了组件的状态和属性等信息。当组件使用 Effect Hook 添加副作用时,会创建一个 Effect Hook 节点,这个节点中包含了副作用的回调函数和依赖数组等信息。当组件更新时,React Fiber 会遍历 Fiber 树,找到与更新相关的 Fiber 节点Effect Hook 节点,并执行相应的更新操作和副作用操作。

React HOOk 闭包陷阱

React Hook 闭包陷阱是指在使用 React Hook 时,由于闭包的特性,可能会导致某些 Hook 的状态无法得到正确地更新,进而导致应用程序出现问题。即:就是 useEffect 等 hook 里用到了某个 state,但是没有加到 deps 数组里,这样导致 state 变了却没有执行新传入的函数,依然引用的之前的 state。所以要正确设置 deps 数组

hook 的实现原理

  • hooks 的原理:hooks 就是在 fiber 节点上存放了 memorizedState 链表,每个 hook 都从对应的链表元素上存取自己的值,每个 hook 是存取各自的那个 memorizedState 来完成自己的逻辑
  • hook 链表有创建和更新两个阶段,也就是 mount 和 update,第一次走 mount 创建链表,后面都走 update。
  • update 时会取出新传入的 deps 和之前存在 memorizedState 的 deps 做对比,如果没有变,就直接用之前传入的那个函数,否则才会用新的函数。

常见的闭包陷阱与解决方案

  • 在 useEffect 中使用闭包变量时,可能会导致 useEffect 钩子无法获取到最新的状态值,进而导致应用程序出现问题。所有一般是使用 useEffect 的依赖项来触发更新 或 将状态值作为 useEffect 的参数传递进去
  • 在 useState 中使用函数更新状态时,由于闭包的特性,可能会导致某些状态值无法得到正确地更新。为了避免这个问题,可以使用 useState 的回调函数来更新状态,或者使用 useReducer 来代替 useState。
  • 在 useMemo 和 useCallback 中使用闭包变量时,可能会导致缓存的值无法得到正确地更新,进而导致应用程序出现问题。为了避免这个问题,可以将需要使用的状态值作为依赖项传递进去,或者使用 useRef 来代替 useState。

试验阶段的 React API 的例子

  • Suspense
js
<SuspenseList revealOrder="forwards">
  <Suspense fallback={'Loading...'}>
    <ProfilePicture id={1} />
  </Suspense>
  <Suspense fallback={'Loading...'}>
    <ProfilePicture id={2} />
  </Suspense>
  <Suspense fallback={'Loading...'}>
    <ProfilePicture id={3} />
  </Suspense>
  ...
</SuspenseList>
  • useDeferredValue
  • useTransition
js
const [isPending, startTransition] = useTransition()

React 大概得运行流程:

  • 初始化:React 通过 ReactDOM.render() 方法将组件渲染到指定的 DOM 节点上。在这个过程中,React 会先创建虚拟 DOM,然后将虚拟 DOM 转换为真实的 DOM 并插入到指定节点上。
  • 渲染阶段:React 会遍历组件树,根据组件的属性和状态生成对应的虚拟 DOM。在这个过程中,如果组件的属性或状态发生了变化,React 会重新生成虚拟 DOM。
  • 协调阶段:在渲染阶段结束后,React 会进入协调阶段。在这个阶段,React 会比较前后两次生成的虚拟 DOM,并计算出最小的更新量。然后,React 会将这些更新应用到真实的 DOM 上,以更新页面内容。
  • 生命周期:React 组件具有生命周期,包括挂载、更新和卸载三个阶段。在不同的生命周期阶段,React 会调用不同的生命周期方法,以便开发者在这些方法中执行一些自定义的操作,如初始化状态、发送网络请求、清理资源等。
  • 事件处理:React 采用了合成事件的方式处理用户事件。在事件处理过程中,React 会将事件封装成 SyntheticEvent 对象,并提供一些方法和属性,以便开发者在事件处理函数中获取相关信息。

ReactDOM.render-->ReactDOMBlockingRoot-->updateContainer(更新容器,react-reconciler 包中, 它串联了 react-dom 与 react-reconciler)-->scheduleUpdateOnFiber

创建 ReactDOMRoot 实例:ReactDOM.createRoot-->(原型上有 render,unmount)-->createRootImpl(创建 fiberRoot 对象)-->createFiberRoot(创建 React 应用首个 Fiber 对象,fiber.tag = HostRoot,其中 fiber.mode 属性, 会与 3 种 RootTag(ConcurrentRoot,BlockingRoot,LegacyRoot)关联起来)-->

创建 ReactDOMBlockingRoot 实例:ReactDOM.createBlockingRoot-->(原型上有 render,unmount)-->createRootImpl(创建 fiberRoot 对象)

注意:fiber 树中所有节点的 mode 都会和 HostRootFiber.mode 一致(新建的 fiber 节点, 其 mode 来源于父节点),所以 HostRootFiber.mode 非常重要, 它决定了以后整个 fiber 树构建过程.

扩展阅读