react 源码-concurrent mode
concurrent mode react17 开始支持concurrent mode,这种模式的根本目的是为了让应用保持 cpu 和 io 的快速响应,它是一组新功能,包括 Fiber、Scheduler、Lane,可以根据用户硬件性能和网络状况调整应用的响应速度,核心就是为了实现异步可中断的更新。concurrent mode 也是未来 react 主要迭代的方向。
cup:让耗时的 reconcile 的过程能让出 js 的执行权给更高优先级的任务,例如用户的输入,
io:依靠 Suspense
FiberFiber我们之前介绍过,这里我们来看下在 concurrent mode 下 Fiber 的意义,react15 之前的 reconcile 是同步执行的,当组件数量很多,reconcile 时的计算量很大时,就会出现页面的卡顿,为了解决这个问题就需要一套异步可中断的更新来让耗时的计算让出 js 的执行权给高优先级的任务,在浏览器有空闲的时候再执行这些计算。所以我们需要一种数据结构来描述真实 dom 和更新的信息,在适当的时候可以在内存中中断 reconcile 的过程 ...
react 源码-scheduler & lane
SchedulerScheduler主要的功能是时间切片和调度优先级
时间切片在浏览器的一帧中 js 的执行时间如下
requestIdleCallback是在浏览器重绘重排之后,如果还有空闲就可以执行的时机,所以为了不影响重绘重排,可以在浏览器在requestIdleCallback中执行耗性能的计算,但是由于requestIdleCallback存在兼容和触发时机不稳定的问题,scheduler中采用MessageChannel来实现requestIdleCallback,当前环境不支持MessageChannel就采用setTimeout。
在之前的介绍中我们知道在performUnitOfWork之后会执行render阶段和commit阶段,如果在浏览器的一帧中,cpu 的计算还没完成,就会让出 js 执行权给浏览器,这个判断在workLoopConcurrent函数中,shouldYield就是用来判断剩余的时间有没有用尽。在源码中每个时间片时5ms,这个值会根据设备的fps调整。
12345function workLoopConcurrent() { w ...
react源码-hook
hookhook调用入口在hook源码中hook存在于Dispatcher中,Dispatcher就是一个对象,不同hook 调用的函数不一样,全局变量ReactCurrentDispatcher.current会根据是mount还是update赋值为HooksDispatcherOnMount或HooksDispatcherOnUpdate
1234ReactCurrentDispatcher.current = current === null || current.memoizedState === null//mount or update ? HooksDispatcherOnMount : HooksDispatcherOnUpdate;
12345678910111213141516171819202122232425const HooksDispatcherOnMount: Dispatcher = {//mount时 useCallback: mountCallback, useContext: readContext, useEffect: m ...
react源码-diff
diff 在render阶段更新Fiber节点时,我们会调用reconcileChildFibers对比current Fiber和jsx对象构建workInProgress Fiber,这里current Fiber是指当前dom对应的fiber树,jsx是class组件render方法或者函数组件的返回值。
12345678910111213141516171819202122232425262728293031323334353637function reconcileChildFibers( returnFiber: Fiber, currentFirstChild: Fiber | null, newChild: any,): Fiber | null { const isObject = typeof newChild === 'object' && newChild !== null; if (isObject) { switch (newChild.$$typeof) { ca ...
react源码-commit阶段
commit 阶段 在render阶段的末尾会调用commitRoot(root);进入commit阶段,这里的root指的就是fiberRoot,然后会遍历render阶段生成的effectList,effectList上的Fiber节点保存着对应的props变化。之后会遍历effectList进行对应的dom操作和生命周期、hooks回调或销毁函数,各个函数做的事情如下
在commitRoot函数中其实是调度了commitRootImpl函数
12345function commitRoot(root) { var renderPriorityLevel = getCurrentPriorityLevel(); runWithPriority$1(ImmediatePriority$1, commitRootImpl.bind(null, root, renderPriorityLevel)); // 这个函数优先级非常高,会同步的去执行 return null;}
在commitRootImpl的函数中主要分三个部分
mutation前
调用flu ...
react源码-render阶段
render 阶段render阶段的主要工作是构建Fiber树和生成effectList,在第5章中我们知道了react入口的两种模式会进入performSyncWorkOnRoot或者performConcurrentWorkOnRoot,而这两个方法分别会调用workLoopSync或者workLoopConcurrent
1234567891011function workLoopSync() { while (workInProgress !== null) { performUnitOfWork(workInProgress); }}function workLoopConcurrent() { while (workInProgress !== null && !shouldYield()) { performUnitOfWork(workInProgress); }}
这两函数的区别是判断条件是否存在shouldYield的执行,如果浏览器没有足够的时间, ...
react源码-state触发状态更新
state 触发状态更新重点看下 setState forceUpdate
this.setState内调用this.updater.enqueueSetState
123456789Component.prototype.setState = function (partialState, callback) { if (!(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null)) { { throw Error( "setState(...): takes an object of state variables to update or a function which returns an object of state variables. " ); } } // 主要调用了这个方法 this.u ...
ngrok
安装 go安装地址
环境变量123456export GO111MODULE=offexport GOPROXY=https://goproxy.cnexport GOROOT=/usr/local/go #当前自己的golang所在位置export PATH=$PATH:/usr/local/go/bin::$GOROOT/bin:/usr/local/ngrok/binexport GOPATH=/usr/local/ngrok/export NGROK_DOMAIN="ngrok.wuzhaoyi.xyz"
安装 ngrok12cd /usr/localgit clone https://github.com/inconshreveable/ngrok.git
创建证书1234567891011cd ngrokopenssl genrsa -out rootCA.key 2048openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=ngrok.wuzhaoyi.xyz" -da ...
react源码-Fiber
Fiber react15在render阶段的reconcile是不可打断的,这会在进行大量dom的reconcile时产生卡顿,因为浏览器所有的时间都交给了js执行,并且js的执行时单线程。为此react16之后就有了scheduler进行时间片的调度,给每个task一定的时间,如果在这个时间内没执行完,也要交出执行权给浏览器进行绘制和重排,所以异步可中断的更新需要一定的数据结构在内存中来保存dom的信息,这个数据结构就是Fiber(虚拟dom)。
Fiber的数据结构12345678910111213141516171819202122232425262728293031323334353637383940414243function FiberNode( tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode,) { //保存节点的信息 this.tag = tag;//对应组件的类型 this.key = key;//key属性 this.elementType = ...
react源码-入口函数
react中的入口函数的模式
legacy12345678910111213141516171819202122232425262728render() => // 判断root 节点存不存在, 不存在 legacyRenderSubtreeIntoContainer() => { legacyCreateRootFromDOMContainer() => { // 首先ssr渲染相关逻辑 var shouldHydrate = forceHydrate || shouldXXXHeuristic(container); // 中间代码... 然后 return createLegacyRoot(container, shouldHydrate? { hydrate: true } : undefined) => { return new ReactDOMBlockingRoot(container, LegacyRoot, options) ...