实现一个 Mini React:核心功能详解 - useRef 的实现, 从底层理解运行机制

150 阅读2分钟

xdm,又要到饭了,又更新代码了!

总结一下上一篇完成的内容,

  1. 完成了useLayoutEffect的实现, 从底层理解运行机制。

有兴趣的可以点这里查看useLayoutEffect的实现, 从底层理解运行机制

这一章节我们实现一个 useRef。

useRef hook 接受一个value ,具体使用场景有:

  1. value 更新但是不想组件更新,即数据更新不驱动ui更新
  2. 获取原生dom,进一步进行操作
  3. 其他

对于前2个使用场景大家一定不陌生,那么我们就从上面的第一点开始实现,这个的实现非常简单,即fiber的ref数组的对应值更新但是不驱动调和过程(reconcile 阶段)那么就会保持值的更新,ui不会更新。

ref的实现

function useRef(initialValue) {
  const oldHook =
    workInProgress.alternate &&
    workInProgress.alternate.hooks &&
    workInProgress.alternate.hooks[workInProgress.currentHook];

  const hook = oldHook ? oldHook : { current: initialValue };

  workInProgress.hooks.push(hook);
  workInProgress.currentHook++;

  return hook;
}

  1. 从current fiber树(上次渲染的)获取对应fiber上hook数组里对应的ref值,
  2. 如果可以获取,则根据这个值创建hook,添加进当前的fiber(这个fiber指的 work in progress 树对应的fiber)
  3. 如果首次渲染,则创建一个对象,设置一个属性,current, 值就是initialValue, 这也就是 useRef 的值使用current 获取。
  4. 更新currentHook 索引
  5. 返回从current fiber 树对应的fiber上hook数组里的对应值,或者第3点创建的新对象

这个就是ref 更新其值不会更新ui的原理,即没有启动调和过程。 react 管理了跨渲染周期始终不启动调和过程。这可以保证数据更新但是ui不更新。

ref的实现 - 支持获取原生dom

支持获取原生dom其实也是从对应的fiber上面获取,实现这个需要修改我们前面讲解fiber实现使用的几个函数,

function createDom(fiber) {
    const dom = fiber.type === TEXT_ELEMENT ? document.createTextNode('') : typeof fiber.type === 'string' ? document.createElement(fiber.type) : null
    
    if (fiber.props && fiber.props.ref) {
        fiber.props.ref.current = dom
    }
    
    updateDom(dom, {}, fiber.props)
    
    return dom
}

判断props有没有ref,如果有,则把创建好的dom 挂在fiber.props.ref.current上面。由于children / ref 属于特殊的props,我们根据props配置数据给真实dom需要忽略这2个props,

function updateDom(dom, prevProps, nextProps) {
    if (!dom) 
        return
        
    Object.keys(prevProps).forEach(name=>{
        if (name === 'children' || name === 'ref') {
            // 忽略
        } 
        // 其他代码不变
    })
}

updateDom 已经在前面的篇章实现过了,上面代码添加了对于ref的处理。

至此,我们对于常用ref的场景,进行了源码的实现,对于你之后处理ref数据一定有了更深的理解。

这篇我们尝试实现了一个useRef版本,讲解了它的使用场景以及底层实现原理。

那么下一篇将从0-1实现开发 lazy , 组件懒加载 。

如果文章对你有帮助,请点个赞支持一下!

啥也不是,散会。