济宁网站建设 济宁智雅,思帽网站建设,asp.net怎样做网站登录,唐山哪个公司可以建网站写在前面
网上有许多关于react源码解读的文章#xff0c;其中有很多都只是单纯贴源码#xff0c;罗列变量名。其实大家都知道这个英文怎么读#xff0c;直译也大概知道意思#xff0c;但是这个英文在react中起到什么作用#xff0c;并没有说的很通俗明白。
对于刚刚接触…写在前面
网上有许多关于react源码解读的文章其中有很多都只是单纯贴源码罗列变量名。其实大家都知道这个英文怎么读直译也大概知道意思但是这个英文在react中起到什么作用并没有说的很通俗明白。
对于刚刚接触源码或者想要了解react实现的人来说没有起到引导作用一堆函数变量反而劝退了很多人。
所以打算开启一个系列的文章用简单的代码片段代替源码拆解react的时间分片、优先级调度、diff等核心模块让大家一眼就能明白其中的原理。
react15为什么需要进化
react15有两大原罪渲染阻塞和无法合并异步函数里面的setState
原罪1同步渲染阻塞主线程
react15从setState到DOM节点渲染到页面上整个流程都是同步的所以如果其中某个环节占用时间特别长就会造成主线程阻塞。
由于JS的执行是单线程的JS线程与浏览器的其他线程互斥如果JS线程阻塞浏览器的渲染线程、事件线程也会相应的挂起。此时用户触发的浏览器原生事件也会无响应造成卡顿的现象。
疑问react15什么情况下会造成阻塞
react15采用的是树形结构的虚拟DOM树使用了递归方式的进行节点遍历递归意味着虚拟DOM树的构建是一个同步的过程只要一开始就无法中断。而且DOM节点层级越深节点数越多diff流程霸占JS线程的时间就越长。
当然网上都是这么说实际上是不是真的是树形结构是不是真的用递归的方式进行节点遍历还是需要经过实际源码考证为此我翻看了react15.5.3的源码
求证1树形结构
div key{最外层节点}{[a, b, c, d].map( (v,index) div key{第一层子节点 - ${v}}span key{${v}的子节点}parentNode{v}/span/div)}
/div上面JSX代码在转换为DOM树结构时是通过树形的结构进行层层遍历 求证2递归遍历
这里采用伪代码的形式模拟react15的节点遍历具体源码调用层级跨度大贴代码不好分析有兴趣的同学可以翻看真正的源码查看具体细节 function 构建节点(节点) {if (有子节点) return 生成子节点(节点)return 节点}function 生成子节点(children) {const 子节点列表 []children.map(child {子节点列表.push(构建节点(child))})return 子节点列表}function 挂载节点(node) {container.insertBefore(node)}function Render(组件, container) {const 应用根节点 组件()const 节点树 构建节点(应用根节点)挂载节点(节点树, container)}function Count(params) {return div1div}Render(Count/, document.querySelector(#root))可以看到当遍历到一个节点发现下面有子节点的时候他会递归调用构建节点的方法继续往下构建DOM树整个DOM树构建的过程都是同步的。
原罪2无法合并异步函数里面的setState
除了阻塞react15下setState的合并更新机制是以函数为单位将函数内同步执行的setState合并注意是同步执行的setState这样会出现一个问题异步函数中的setState无法被合并。
问题1异步函数中的setState更新会以同步的形式呈现问题2异步函数内的每一个setState都会触发一次完整的视图更新造成性能损耗 下面展示一下问题代码
state { count: 0 }
setCount() {this.setState({ count: 1 })console.log(this.state.count) // 输出0这里是正常的state不会马上更新setTimeout(() {this.setState({ count: 2 })console.log(this.state.count) // 输出2state同步更新没有被合并})
}上面的的代码为什么会输出这样的结果react15 的合并更新是怎么实现的呀
卖个关子我会在后面的系列文章中为你解答用30行代码告诉你 react15 合并更新原理
Fiber架构下的react得到哪些提升
为解决react15的痛点在16版本后react重写整个架构为的就是实现异步可中断更新。异步可中断更新这几个字说着简单那具体需要怎么实现呢
回顾react15的两大痛点我们需要解决两件事情
解决阻塞问题。让setState在异步函数里面也能被合并。 下面将一一解决这两个问题
解决阻塞问题
看完上面react15节点遍历的伪代码不难发现阻塞的根源有两个
递归遍历节点树无法中断遍历遍历节点树会一直占用主线程阻塞了浏览器的其他线程
解决手段1改变树结构和节点遍历方式
react15使用了树形结构串联整棵树这也间接导致react15采用递归子节点for循环的方式对虚拟DOM树进行层层遍历过程无法中断。
要实现可中断的遍历好办不用递归改用while遍历的话就能满足中断这个要求
但是树形结构不方便做while遍历啊嵌套层级深分支又多那咋整
把整棵树拍扁用链表的形式描述树结构这样我就能无需维护多余的变量记录维护遍历顺序非常轻松的一个个遍历节点通过while循环做遍历中断也会更加清晰
下面我用伪代码的形式简单模拟一下react16的遍历
let 需要被遍历的幸运儿节点 null
function 构建节点() {/** * ...在这里进行节点构建工作 */需要被遍历的幸运儿节点 需要被遍历的幸运儿节点.next
}
function 节点遍历() {while (需要被遍历的幸运儿节点 ! null) {构建节点()}
}
function 调度() {需要被遍历的幸运儿节点 react应用根节点节点遍历()
}
调度()相关参考视频讲解进入学习
注意需要被遍历的幸运儿节点 需要被遍历的幸运儿节点.nextreact并不是简简单单用next去描述节点关系我会在后面系列文章中详细描述
解决手段2时间分片
好了终于实现了可中断的更新我们算是完成了半个react16了还差一个异步怎么做呢那就是时间分片
时间分片顾名思义就是设定一个固定而连续且有间隔的时间区间好像不那么顾名思义
什么是固定就是我每天固定摸鱼工作8小时
什么是连续我每天都需要上班
什么是有间隔周末休息
在 react 的 时间分片对应的就是
时间分片固定的5毫秒左右会根据优先级有所浮动求生欲分片支配着react工作的中断和开启其实只是作用于部分工作分片与分片之间是有间隔的这段间隔就是让浏览器有空闲时间去处理其他线程的任务
下面简单实现一下时间分片
下一章再讲吧一下子写太多怕消化不了逃时间分片在performance中的直观体现基本都控制在5毫秒左右 让setState在异步函数里面也能被合并
react16对于这一块的实现是基于整个Fiber架构的设计实现的需要对时间分片、异步调度、lane优先级机制、state计算方式、事件系统有一定前置知识或者能更好去理解
这里我简述下实现的原理 每一次执行setState a. 将此次更新的优先级关联到当前Fiber节点和根Fiber节点 b. 执行调度函数 调度函数会先进行一个逻辑判断判断当前应用根节点的优先级和当前已被调度的优先级是否相等 a. 相等。是同一个函数下面的setState可以合并更新不重复发起协调任务 b. 不相等。发起协调任务
这里不相等分为两种情况一种是第一次发起调度一种是高优先级任务进来。
如果对源码有一定了解小伙伴可能会有点点明白我这里说的是什么意思上面说的并不完全与源码一一对应但大概逻辑是相通的后面我会以更详细的篇幅给大家理清楚优先级调度。
宏观角度了解react的新架构
系列第一篇主要是为大家理解react16源码做一个前置知识的铺垫让大家对react16的构成有一个大概的了解下面是一张react16的模块功能分布图 Scheduler
Scheduler主要负责react的任务调度其中包括分片调度和优先级调度 分片调度的主要任务是负责reconcile (render)阶段能够间断执行节点遍历任务 优先级调度主要是为了将react任务划分为多种优先级类型能够实现高优先级任务快速响应
Reconciler
Reconciler主要负责Fiber节点的构建和创建相应的副作用 state计算在引入了优先级机制后并不是简单的将state计算覆盖其中关联到低优先级任务重启的逻辑 diff就是通过遍历新旧Fiber树找出需要增删改的节点 副作用创建将需要增删改的节点以位与运算的形式记录到Fiber节点的flags属性上等待commit阶段清除这些副作用副作用包含但不限于节点增删改还有useEffect执行ref更新等等的副作用
Renderer
Renderer (commit)阶段做的事情就是清除副作用然后开启下一轮的调度
以上就是react的基本构成和各个模块的职责。后续为了更方便进行解读我会用render阶段代指Reconciler用commit阶段代指Renderer
写在最后
本文主要简述了react的进化历程和新react架构的基本构成。下一篇我会讲讲react的时间分片同时会结合react的任务去模拟一个时间分片的运行过程。
上文所述如果有说的不对的望各位大佬可以包涵指正。如果有不懂的可以把疑问点提出来我会逐一解答。每一次交流的过程都是一次思想和学习的碰撞大家可以尽情diss