IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 【面试准备:react】hooks的原理 -> 正文阅读

[JavaScript知识库]【面试准备:react】hooks的原理

前言:

hooks就是解决函数组件没有state,生命周期,逻辑不能复用的一种技术方案。

renderWithHooks函数的作用是调用function组件函数的主要函数。

function组件初始化:

renderWithHooks(
null, // current Fiber
workInProgress, // workInProgress Fiber
Component, // 函数组件本身
props, // props
context, // 上下文
renderExpirationTime,// 渲染 ExpirationTime
);
对于renderwithHooks执行funtion初始化时,current是没有的,之后完成一次组件更新后,会把当前workInProgress树赋值给current树

function组件更新:

renderWithHooks(
current,
workInProgress,
render,
nextProps,
context,
renderExpirationTime,
);

那么renderwithHooks做了些什么呢?

在react-r econciler/src/ReactFiberHooks.js中

export function renderWithHooks(
  current,
  workInProgress,
  Component,
  props,
  secondArg,
  nextRenderExpirationTime,
) {
  renderExpirationTime = nextRenderExpirationTime;
  currentlyRenderingFiber = workInProgress;

  workInProgress.memoizedState = null;
  workInProgress.updateQueue = null;
  workInProgress.expirationTime = NoWork;

  ReactCurrentDispatcher.current =
      current === null || current.memoizedState === null
        ? HooksDispatcherOnMount
        : HooksDispatcherOnUpdate;

  let children = Component(props, secondArg);

  if (workInProgress.expirationTime === renderExpirationTime) { 
       // ....这里的逻辑我们先放一放
  }

  ReactCurrentDispatcher.current = ContextOnlyDispatcher;

  renderExpirationTime = NoWork;
  currentlyRenderingFiber = null;

  currentHook = null
  workInProgressHook = null;

  didScheduleRenderPhaseUpdate = false;

  return children;
}

current fiber树:当完成一次渲染之后,会产生一个current树current会在commit阶段替换成真实的Dom树

workInProgress fiber树:即将调和渲染的fiber树。在一次新的组件更新过程中,会从current复制一份作为workInProgress,更新完毕后,将当前的workInProgress树赋值给current树。

workInProgress.memoizedState: 在class组件中,memoizedState存放state信息,在function组件中,这里可以提前透漏一下,memoizedState在一次调和渲染过程中,以链表的形式存放hooks信息。
workInProgress.expirationTime: react用不同的expirationTime,来确定更新的优先级。
currentHook:可以理解current树上的指向的当前调度的hooks节点
workInProgressHook:可以理解 workInProgressHook 树上指向的当前调度的hooks节点。

回到原点,renderWithHooks函数的主要作用:
1、置空workInProgress树的memoizedState和updateQueue,因为要把新的hooks信息挂载到这两个属性上,然后在组件的 commit阶段,将workInProgress树替换成current树,替换真实的DOM元素节点,并在current树保存hooks信息
2、根据当前函数是否是第一次渲染,赋予ReactCurrentDispatcher.current不同的hooks(是onMount还是onUpdate),第一次渲染组件,那么用的是HooksDispatcherOnMount hooks对象。
对于渲染后,需要更新的函数组件,则是HooksDispatcherOnUpdate对象,那么两个不同就是通过current树上是否memoizedState(hook信息)来判断的。如果current不存在,证明是第一次渲染函数组件。
3、调用Component(props,secondArg);执行函数组件,函数组件在这里真正的被执行了,然后hooks被依次执行,把hooks信息依次保存到workInProgress树上
4、将ContextOnlyDispatcher赋值给ReactCurrentDispatcher.current,也就是说我们没有在函数组件中,调用的hooks,都是ContextOnlyDispatcher对象上hooks
5、最后置空一些变量,比如currentHook,currentlyRenderingFiber,workInProgressHook

HooksDispatcherOnMount 和 HooksDispatcherOnUpdate
上面讲到了会根据函数是否是第一次渲染,给current赋值不同的hooks,接下来具体讲一下这两个hooks

const HooksDispatcherOnMount = {
  useCallback: mountCallback,
  useEffect: mountEffect,
  useLayoutEffect: mountLayoutEffect,
  useMemo: mountMemo,
  useReducer: mountReducer,
  useRef: mountRef,
  useState: mountState,
};

更新组件

const HooksDispatcherOnUpdate = {
  useCallback: updateCallback,
  useEffect: updateEffect,
  useLayoutEffect: updateLayoutEffect,
  useMemo: updateMemo,
  useReducer: updateReducer,
  useRef: updateRef,
  useState: updateState
};

useState、useEffect、useRef、useMemo介绍和原理:

function mountWorkInProgressHook() {
  const hook: Hook = {
    memoizedState: null,  // useState中 保存 state信息 | useEffect 中 保存着 effect 对象 | useMemo 中 保存的是缓存的值和deps | useRef中保存的是ref 对象
    baseState: null,
    baseQueue: null,
    queue: null,
    next: null,
  };
  if (workInProgressHook === null) { // 例子中的第一个`hooks`-> useState(0) 走的就是这样。
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    workInProgressHook = workInProgressHook.next = hook;
  }
  return workInProgressHook;
}

mountWorkInProgressHook,首先每次执行一个hooks函数,都产生一个hook对象,里面保存了当前hook信息,将每个hooks以链表形式串联起来,并赋值给workInProgress的memosizedState.
为什么不能在条件语句中写hooks?
因为在下一次函数组件更新,hooks链表结构会被破坏current树的memoizedState缓存hooks信息,和当前workInProgress不一致,如果涉及到读取state等操作,就会发生异常。
hooks通过什么来证明唯一性? 通过hooks链表顺序。

mouted阶段 hooks总结

在一个函数组件第一次渲染执行上下文过程中,每个react-hooks执行,都会产生一个hook对象,并形成链表结构,绑定在workInProgress的memoizedState属性上,然后react-hooks上的状态,绑定在当前hooks对象的memoizedState属性上。对于effect副作用钩子 ,会绑定在workInProgress.updateQueue上,等到commit阶段,dom树构建完成,在执行每个effect副作用钩子。

hooks更新阶段
…省略 ,暂时没看

hooks挂载数据的数据结构叫做fiber
在16版本前,是直接遍历vdom,通过dom api增删改dom的方式来渲染的。引入了fiber架构后,把vdom树转换成fiber链表,然后再渲染fiber。
那么hooks挂载在哪个fiber节点呢? hooks挂载在renderwithHooks函数中的workInProgress对象上,保存在workInProgress的memorizedState上,它是一个通过next串联的链表。

useEffect、useState的实现相对要复杂一点,因为涉及到调度,hooks会把这些effect串联成一个updateQueue的链表

总结

-class 支持 state 属性和生命周期方法,而 function 组件也通过 hooks api 实现了类似的功能。
-hooks 的实现就是基于 fiber 的,会在 fiber 节点上放一个链表,每个节点的 memorizedState 属性上存放了对应的数据,然后不同的 hooks api 使用对应的数据来完成不同的功能。
-所有 hooks api 都是基于 fiber 节点上的 memorizedState 链表来存取数据并完成各自的逻辑的。
-几个简单的 hooks: useRef、useCallback、useMemo,它们只是对值做了缓存,逻辑比较纯粹,没有依赖 React 的调度。而 useState 会触发 fiber 的 schedule,useEffect 也有自己的调度逻辑。实现上相对复杂一些,我们没有继续深入。

这次只是浅学了一下,后面打算再多深入学习,这里面还有很多具体的不太明白
参考:1、React Hooks 的原理,有的简单有的不简单
2、「react进阶」一文吃透react-hooks原理

建议先阅读2,讲得更详细,再看1熟悉一边,1讲的更概括简洁。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-05 23:27:51  更:2022-07-05 23:29:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/11 11:51:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码