likes
comments
collection
share

Mini-React

作者站长头像
站长
· 阅读数 6

八天实现的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
评论
请登录