Mini-React
八天实现的Mini-React
按照官方React的api的设计风格,实现了简易版的mini-react。 通过render函数将VDom渲染成真实dom,为了比较更新前后dom的数据变化,引入fiber架构,将tree转化为链表结构,fiber数据结构设计为type节点类型,props 属性,dom dom元素,parent 父级节点,sibling 兄弟节点,child 子节点,effectTag 更新类型,alternate 旧Dom,更方便的比较新老dom的变化,不需要递归遍历两棵dom树。对于普通节点和函数组件节点,采用不同的处理方式,更新dom属性和chilren节点。 实现了useState,useEffect等关键api,其中,通过闭包保存当前节点的处理方式尤为深刻。有很多设计上的细节还是需要再仔细琢磨。
useState Api
const useState = (initial) => {
let currentFiber = wipFiber
const oldstateHook = currentFiber.alternate?.hookArr[hookIndex]
const stateHook = {
state: oldstateHook ? oldstateHook.state : initial,
queue: oldstateHook ? oldstateHook.queue : []
}
stateHook.queue.forEach(action => {
stateHook.state = action(stateHook.state)
})
stateHook.queue.length = 0
hookIndex++
hookArr.push(stateHook)
currentFiber.hookArr = hookArr
const setState = (action) => {
//检测修改后的值是否和之前一样 一样的话就不需要渲染
const eagerState=typeof action === 'function'?action(stateHook.state):action
if(eagerState===stateHook.state)return
stateHook.queue.push(typeof action === 'function' ? action : () => action)
wipRoot = {
...currentFiber,
alternate: currentFiber
}
fiberOfUnit = wipRoot
}
return [stateHook.state, setState]
}
useEffect Api
let effectHooks=[]
const useEffect = (callback, dependencies) => {
let currentFiber = wipFiber
const effectHook={
callback,
deps:dependencies,
cleanup:null
}
effectHooks.push(effectHook)
currentFiber.effectHooks=effectHooks
}
Effect函数执行
const commitEffect=()=>{
function run(fiber){
// console.log('commitEffect-fiber',fiber);
if(!fiber)return
if(!fiber.alternate){
//新增
fiber.effectHooks?.forEach((hook)=>{
hook.cleanup= hook.callback()
})
}else{
//编辑
//判断依赖值有没有改变 改变才调用
const oldEffecHooks=fiber?.alternate?.effectHooks
fiber?.effectHooks?.forEach((newHook,index)=>{
if(newHook.deps.length===0)return
newHook.deps.forEach((newDep,idx)=>{
if(newDep!==oldEffecHooks[index].deps[idx]){
newHook.cleanup= newHook.callback()
}else{
newHook.cleanup= oldEffecHooks[index].cleanup
}
})
})
}
run(fiber.child)
run(fiber.sibling)
}
run(wipRoot)
}
清除Effect函数副作用
const commitCleanup = () => {
function runCleanup(fiber) {
if(!fiber)return
// fiber.alternate?.effectHooks?.forEach((hook)=>{
// console.log('commitCleanup',hook);
// hook?.cleanup()
// })
if(fiber.alternate){
const oldEffecHooks=fiber?.alternate?.effectHooks
fiber?.effectHooks?.forEach((newHook,index)=>{
if(newHook.deps.length===0)return
newHook.deps.forEach((newDep,idx)=>{
if(newDep!==oldEffecHooks[index].deps[idx]){
oldEffecHooks[index]?.cleanup?.()
}
})
})
}
runCleanup(fiber.child)
runCleanup(fiber.sibling)
}
runCleanup(wipRoot)
}
最后,实现了todoList小案例。在这个过程中,遇到了很多问题,不管是自己思考、断点解决或者是找群友答疑,还好最终坚持下来了。非常感谢崔老师的辛苦付出和专业指导,很喜欢这种授课方式,希望可以继续下去,mini-koa我还来!
代码:mini-react
转载自:https://juejin.cn/post/7352762865072259091