y805939188 / simple-react

this is a simple react framework

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Simple React

简介 : 简化版的react, 虽然不能真的用在项目里, 但是可以作为React源码的阅读笔记看求各位大佬给星星!感激不尽
已完成 : fiber架构, setState, 事件代理, ContextAPI, RefAPI, useState
正在进行 : Concurrent异步渲染(渣方法实现了个渣版本 正在完善中...)
跑起来 : 根目录下执行 webpack-dev-server
带注释的源码放在procedure目录下了

源码阅读笔记

点这里
 许久没更新了, 加两个知乎回答链接~
如何用hooks实现class组件this.setState的callback
react源码中, reactRoot, fiberRoot, rootFiber的关系

React渲染流程

        React16对于React15多了一个新的Fiber架构, 每次对比也都是对比的Fiber数据结构。
        当调用ReactDOM.render时,首先React会在内部创建出一个Root对象,Root对象是整个React应用的根儿。每次React开始调度,不管是初次渲染还是setState都是从这个Root根儿上开始的。
        创建好了Root后还会同时创建一个未初始化的Fiber对象,也就是uninitialFiber对象。其实每次React更新都是要对比新旧的Fiber,初次渲染的时候也是要对比新旧Fiber的,但是又因为初次渲染时根本没有上一次的Fiber,所以React才会在一开始就自己创建出一个未初始化也就是啥状态都没有uninitialFiber来假装有上一次的状态,之后才会为本次渲染真正创建一个属于初次渲染的RootFiber,之后用这个RootFiber和刚才那个uninitialFiber作对比。到这儿为止都是React自己干的事儿,和我们用React的人传进来的参数一点关系都没有。
        当Root和uninitialFiber以及RootFiber都创建好了,才会真正开始初次的渲染。
        开始渲染后,会从ReactDOM.render传进去的第一个组件开始循环调度,为根组件下以及根组件等每一个节点不管是原生dom节点,还是函数组件或是类组件甚至React内部提供的组件都创建一个自己对应的fiber对象,在这个过程中所创建的fiber,叫做workInProgress。每个workInProgress都连接着一个保存着当前节点跟新前状态的fiber,这个前一个状态的fiber叫做current。不过由于是初次渲染,所以只有RootFiber有current也就是uninitialFiber,剩下的所有节点的current都是null。



        这个循环创建(或更新)fiber的过程叫做render阶段,当render阶段结束说明所有的子节点都有了对应的fiber,形成了一颗fiber树,然后就可以进入提交阶段,也就是commit阶段。
        commit阶段会去RootFiber上找产生了更新的fiber,然后一个个地去根据对应的标识去更新。那么RootFiber上的fiber是哪儿来的呢~其实是在render阶段做的。
        因为循环创建workInProgress的过程是一个深度优先的过程,所以会优先给传进来的react元素的一侧创建workInProgress,一侧创建完了再找他的父节点,才去给父节点以及父节点的兄弟节点去创建fiber。而当某一侧的子节点都创建好了之后,会有一个循环来判断刚刚创建好的这个一侧的节点,哪个节点上有更新,对于有更新的节点会被记录到父节点上,这样一层一层地往父节点上记录有更新的子节点,最终就会将全部有更新的节点挂到RootFiber上,形成一条链表。所以在commit阶段就根据这条链表进行对应的增删改的操作。
        这样就完成了初次渲染。之后再执行setState(同步)的话流程基本上差不多,只不过上一轮中创建的RootFiber所领头的那颗workInProgress树则变成了current树,也就是保存着旧状态的fiber树。setState后会重新为本次的更新创建(或复用)新的fiber,也就是新的workInProgress。



        setState时同样会在render阶段找出哪个节点有更新然后挂到父节点上,最终把全部更新挂到RootFiber上。最后commit阶段挨个儿进行更新。
        以上setState只是在同步状态下。使用了Concurrent组件会开启异步模式,中间render过程会有点不一样。同步状态下render阶段会一把梭,异步模式,就回头再说吧。
        知道了React的整体渲染更新流程,再看那些乱七八糟的函数就好看多了~


React函数调用流程

   因为react这个库本身没做啥事儿,就是把JSX肝成React元素之类的(就是那个有$$typeof的就是react元素),主要做事儿的都是react-dom做的,所以就只写一些react-dom的方法。

ReactDOM.render(调用关系基本是按照缩进来的)
legacyRenderSubtreeIntoContainer
updateContainer
  requestCurrentTime
  computeExpirationForFiber
scheduleRootUpdate
  enqueueUpdate
  scheduleWork
   scheduleWorkToRoot
   markPendingPriorityLevel
   requestWork
    addRootToSchedule
    performSyncWork
     performWork(也会走到下面那个performWorkOnRoot中)
    scheduleCallbackWithExpirationTime(未完成)
    performWorkOnRoot
     renderRoot
      createWorkInProgress
       createFiber
      workLoop
       performUnitOfWork
        beginWork
         bailoutOnAlreadyFinishedWork
          createWorkInProgress
         mountIndeterminateComponent
           reconcileChildren
         updateHostRoot
          processUpdateQueue
           reconcileChildren
         updateFunctionComponent
          prepareToUseHooks
          finishHooks
          reconcileChildren
         updateClassComponent
          resolveDefaultProps
          constructorClassInstance
           adoptClassInstance
          mountClassInstance
           processUpdateQueue
            ensureWorkInProgressQueueIsAClone
            getStateFromUpdate
           applyDerivedStateFromProps
          updateClassInstance
          finishClassComponent
           reconcileChildren
         updateHostComponent
           reconcileChildren
         updateHostText
         updateContextProvider
           reconcileChildren
         updateContextConsumer
           reconcileChildren
         completeUnitOfWork
          completeWork
           createInstance
           appendAllChildren
           finalizeInitialChildren
            ensureListeningTo
           diffAndUpdateHostComponent
            prepareUpdate
             diffProperties
          resetChildExpirationTime
     completeRoot
      commitRoot
       commitBeforeMutationLifecycles
        commitPlacement
        commitWork
        commitDeletion
        detachFiber
       commitAllLifeCycles


setState(同步)
enqueueSetState
  requestCurrentTime
  computeExpirationForFiber
  createUpdate
  enqueueUpdate
  scheduleWork
   scheduleWorkToRoot
   (然后接下来的调用关系就跟上有基本一致了)

   这个是同步的方式 异步走的其实也是这个流程
   不过就是在计算时间的那里会计算出一个非Sync的时间
   之后进行到requestWork的时候就走scheduleCallbackWithExpirationTime了


setState(异步)
 更新中
关于Concurrent模式下如何计算优先级
 Concurrent组件主要通过打断workLoop的nextUnitOfWork实现的
 高版本浏览器有个叫requestIdleCallback的API可以在浏览器空闲的时候执行任务
 也就是说可以用requestIdleCallback在浏览器空闲时去执行workLoop中的performUnitOfWork
 但是requestIdleCallback这玩意不好使, 执行频率太低了(也可能是我用错方法了 o(╥﹏╥)o )
 所以react中自己用requestAnimationFrame模拟实现了一个兼容性好并且比较能稳定执行的方法
 这个实现有点复杂, 现在还没太研究明白 (╬ ̄皿 ̄) 所以暂时先用requestIdleCallback简单实现一个
 目前好像有bug ((٩(//̀Д/́/)۶)) 之后慢慢完善吧...

Hooks
useState
  useReducer
useReducer
  createWorkInProgressHook
   cloneHook
   createHook
  dispatchAction
   requestCurrentTime
   computeExpirationForFiber
   scheduleWork
其他Hooks



其他各种API以后会慢慢更新的,如果哪儿说的有啥问题的话请告诉我。顺便请各位大佬随手点个星星!小弟不胜感激~

About

this is a simple react framework


Languages

Language:JavaScript 51.3%Language:HTML 47.0%Language:CSS 1.6%