xdm,又要到饭了,又更新代码了!
总结一下上一篇完成的内容,
- 完成了useLayoutEffect的实现, 从底层理解运行机制。
有兴趣的可以点这里查看useLayoutEffect的实现, 从底层理解运行机制
这一章节我们实现一个 useRef。
useRef hook 接受一个value ,具体使用场景有:
- value 更新但是不想组件更新,即数据更新不驱动ui更新
- 获取原生dom,进一步进行操作
- 其他
对于前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;
}
- 从current fiber树(上次渲染的)获取对应fiber上hook数组里对应的ref值,
- 如果可以获取,则根据这个值创建hook,添加进当前的fiber(这个fiber指的 work in progress 树对应的fiber)
- 如果首次渲染,则创建一个对象,设置一个属性,current, 值就是initialValue, 这也就是 useRef 的值使用current 获取。
- 更新currentHook 索引
- 返回从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 , 组件懒加载 。
如果文章对你有帮助,请点个赞支持一下!
啥也不是,散会。