hooks原理解析

React 是实现了组件的前端框架,它支持 class 和 function 两种形式的组件。

class 组件是通过继承模版类(Component、PureComponent)的方式开发新组件的,继承是 class 本身的特性,它支持设置 state,会在 state 改变后重新渲染,可以重写一些父类的方法,这些方法会在 React 组件渲染的不同阶段调用,叫做生命周期函数。

function 组件不能做继承,因为 function 本来就没这个特性,所以是提供了一些 api 供函数使用,这些 api 会在内部的一个数据结构上挂载一些函数和值,并执行相应的逻辑,通过这种方式实现了 state 和类似 class 组件的生命周期函数的功能,这种 api 就叫做 hooks。

hooks 挂载数据的数据结构叫做 fiber。

那什么是 fiber 呢?

我们知道,React 是通过 jsx 来描述界面结构的,会把 jsx 编译成 render function,然后执行 render function 产生 vdom:
在这里插入图片描述
在 v16 之前的 React 里,是直接递归遍历 vdom,通过 dom api 增删改 dom 的方式来渲染的。但当 vdom 过大,频繁调用 dom api 会比较耗时,而且递归又不能打断,所以有性能问题。
在这里插入图片描述
后来就引入了 fiber 架构,先把 vdom 树转成 fiber 链表,然后再渲染 fiber。
在这里插入图片描述
vdom 转 fiber 的过程叫做 reconcile,是可打断的,React 加入了 schedule 的机制在空闲时调度 reconcile,reconcile 的过程中会做 diff,打上增删改的标记(effectTag),并把对应的 dom 创建好。然后就可以一次性把 fiber 渲染到 dom,也就是 commit。

这个 schdule、reconcile、commit 的流程就是 fiber 架构。当然,对应的这个数据结构也叫 fiber。
hooks 就是通过把数据挂载到组件对应的 fiber 节点上来实现的。

fiber 节点是一个对象,hooks 把数据挂载在哪个属性呢?

我们可以 debugger 看下。

准备这样一个函数组件(代码没啥具体含义,就是为了调试 hooks):

function App() {
  const [name, setName] = useState("guang");
  useState('dong');
 
  const handler = useCallback((evt) => {
      setName('dong');
  },[1]);
 
  useEffect(() => {
    console.log(1);
  });
  
  useRef(1);
 
  useMemo(() => {
    return 'guang and dong';
  })
 
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p onClick={handler}>
          {name}
        </p>
      </header>
    </div>
  );
}

在函数打个断点,运行到这个组件就会断住。

我们看下调用栈:
在这里插入图片描述
上一个函数是 renderWithHooks,里面有个 workingInProgress 的对象就是当前的 fiber 节点:
在这里插入图片描述

fiber 节点的 memorizedState 就是保存 hooks 数据的地方。

它是一个通过 next 串联的链表,展开看一下:

在这里插入图片描述
在这里插入图片描述
这就是 hooks 存取数据的地方,执行的时候各自在自己的那个 memorizedState 上存取数据,完成各种逻辑,这就是 hooks 的原理。

这个 memorizedState 链表是什么时候创建的呢?

好问题,确实有个链表创建的过程,也就是 mountXxx。链表只需要创建一次,后面只需要 update。

所以第一次调用 useState 会执行 mountState,后面再调用 useState 会执行 updateState。

总结
React 支持 class 和 function 两种形式的组件,class 支持 state 属性和生命周期方法,而 function 组件也通过 hooks api 实现了类似的功能。

fiber 架构是 React 在 16 以后引入的,之前是 jsx -> render function -> vdom 然后直接递归渲染 vdom,现在则是多了一步 vdom 转 fiber 的 reconcile,在 reconcile 的过程中创建 dom 和做 diff 并打上增删改的 effectTag,然后一次性 commit。这个 reconcile 是可被打断的,可以调度,也就是 fiber 的 schedule。

hooks 的实现就是基于 fiber 的,会在 fiber 节点上放一个链表,每个节点的 memorizedState 属性上存放了对应的数据,然后不同的 hooks api 使用对应的数据来完成不同的功能。

链表自然有个创建阶段,也就是 mountXxx,之后就不需要再 mount 了,只需要 update。所以每个 useXx 的实现其实都是分为了 mountXxx 和 updateXxx 两部分的。

我们看了几个简单的 hooks:useRef、useCallback、useMemo,它们只是对值做了缓存,逻辑比较纯粹,没有依赖 React 的调度。而 useState 会触发 fiber 的 schedule,useEffect 也有自己的调度逻辑。实现上相对复杂一些,我们没有继续深入。

其实给我们一个对象来存取数据,实现 useRef、useCallback、useMemo 等 hooks 还是很简单的。对于需要调度的,则复杂一些。

对于自定义的 hooks,那个就是个函数调用,没有任何区别。(lint 的规则不想遵守可以忽略)

所有 hooks api 都是基于 fiber 节点上的 memorizedState 链表来存取数据并完成各自的逻辑的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值