Fiber 结构长什么样?
基于时间分片的增量更新需要更多的上下文信息,之前的vDOM tree显然难以满足,所以扩展出了fiber tree(即Fiber上下文的vDOM tree),更新过程就是根据输入数据以及现有的fiber tree构造出新的fiber tree(workInProgress tree)。
FiberNode 上的属性有很多,根据笔者的理解,以下这么几个属性是值得关注的:return、child、sibling(主要负责fiber链表的链接);stateNode;effectTag;expirationTime;alternate;nextEffect。各属性介绍参看下面的class FiberNode
:
class FiberNode { constructor(tag, pendingProps, key, mode) { // 实例属性 this.tag = tag; // 标记不同组件类型,如函数组件、类组件、文本、原生组件... this.key = key; // react 元素上的 key 就是 jsx 上写的那个 key ,也就是最终 ReactElement 上的 this.elementType = null; // createElement的第一个参数,ReactElement 上的 type this.type = null; // 表示fiber的真实类型 ,elementType 基本一样,在使用了懒加载之类的功能时可能会不一样 this.stateNode = null; // 实例对象,比如 class 组件 new 完后就挂载在这个属性上面,如果是RootFiber,那么它上面挂的是 FiberRoot,如果是原生节点就是 dom 对象 // fiber this.return = null; // 父节点,指向上一个 fiber this.child = null; // 子节点,指向自身下面的第一个 fiber this.sibling = null; // 兄弟组件, 指向一个兄弟节点 this.index = 0; // 一般如果没有兄弟节点的话是0 当某个父节点下的子节点是数组类型的时候会给每个子节点一个 index,index 和 key 要一起做 diff this.ref = null; // reactElement 上的 ref 属性 this.pendingProps = pendingProps; // 新的 props this.memoizedProps = null; // 旧的 props this.updateQueue = null; // fiber 上的更新队列执行一次 setState 就会往这个属性上挂一个新的更新, 每条更新最终会形成一个链表结构,最后做批量更新 this.memoizedState = null; // 对应 memoizedProps,上次渲染的 state,相当于当前的 state,理解成 prev 和 next 的关系 this.mode = mode; // 表示当前组件下的子组件的渲染方式 // effects this.effectTag = NoEffect; // 表示当前 fiber 要进行何种更新(更新、删除等) this.nextEffect = null; // 指向下个需要更新的fiber this.firstEffect = null; // 指向所有子节点里,需要更新的 fiber 里的第一个 this.lastEffect = null; // 指向所有子节点中需要更新的 fiber 的最后一个 this.expirationTime = NoWork; // 过期时间,代表任务在未来的哪个时间点应该被完成 this.childExpirationTime = NoWork; // child 过期时间 this.alternate = null; // current 树和 workInprogress 树之间的相互引用 } }
function performUnitWork(currentFiber){ //beginWork(currentFiber) //找到儿子,并通过链表的方式挂到currentFiber上,每一偶儿子就找后面那个兄弟 //有儿子就返回儿子 if(currentFiber.child){ return currentFiber.child; } //如果没有儿子,则找弟弟 while(currentFiber){//一直往上找 //completeUnitWork(currentFiber);//将自己的副作用挂到父节点去 if(currentFiber.sibling){ return currentFiber.sibling } currentFiber = currentFiber.return; } }
Concurrent Mode (并发模式)
Concurrent Mode 指的就是 React 利用上面 Fiber 带来的新特性的开启的新模式 (mode)。react17开始支持concurrent mode,这种模式的根本目的是为了让应用保持cpu和io的快速响应,它是一组新功能,包括Fiber、Scheduler、Lane,可以根据用户硬件性能和网络状况调整应用的响应速度,核心就是为了实现异步可中断的更新。concurrent mode也是未来react主要迭代的方向。
目前 React 实验版本允许用户选择三种 mode:
- Legacy Mode: 就相当于目前稳定版的模式
- Blocking Mode: 应该是以后会代替 Legacy Mode 而长期存在的模式
- Concurrent Mode: 以后会变成 default 的模式
Concurrent Mode 其实开启了一堆新特性,其中有两个最重要的特性可以用来解决我们开头提到的两个问题:
- Suspense[14]:Suspense 是 React 提供的一种异步处理的机制, 它不是一个具体的数据请求库。它是React 提供的原生的组件异步调用原语。
- useTrasition[15]:让页面实现
Pending -> Skeleton -> Complete
的更新路径, 用户在切换页面时可以停留在当前页面,让页面保持响应。相比展示一个无用的空白页面或者加载状态,这种用户体验更加友好。
其中 Suspense 可以用来解决请求阻塞的问题,UI 卡顿的问题其实开启 concurrent mode 就已经解决的,但如何利用 concurrent mode 来实现更友好的交互还是需要对代码做一番改动的。
未来可期
Concurrent Mode只是并发,既然任务可拆分(只要最终得到完整effect list就行),那就允许并行执行,(多个Fiber reconciler + 多个worker),首屏也更容易分块加载/渲染(vDOM森林。
并行渲染的话,据说Firefox测试结果显示,130ms的页面,只需要30ms就能搞定,所以在这方面是值得期待的,而React已经做好准备了,这也就是在React Fiber上下文经常听到的待unlock的更多特性之一。
isInputPending —— Fiber架构思想对前端生态的影响
Facebook 在 Chromium 中提出并实现了 isInputPending() API
,它可以提高网页的响应能力,但是不会对性能造成太大影响。Facebook 提出的 isInputPending API
是第一个将中断的概念用于浏览器用户交互的的功能,并且允许 JavaScript 能够检查事件队列而不会将控制权交于浏览器。
目前 isInputPending API 仅在 Chromium 的 87 版本开始提供,其他浏览器并未实现。
Svelte 对固有模式的冲击
当下前端领域,三大框架React、Vue、Angular版本逐渐稳定,如果说前端行业会出现哪些框架有可能会挑战React或者Vue呢?很多人认为Svelte 应该是其中的选项之一。
Svelte叫法是[Svelte]
, 本意是苗条纤瘦的,是一个新兴热门的前端框架。在开发者满意度、兴趣度、市场占有率上均名列前茅,同时,它有更小的打包体积,更少的开发代码书写,在性能测评中,与React、Vue相比,也不遑多让。
Svelte 的核心思想在于『通过静态编译减少框架运行时的代码量』。
Svelte 优势有哪些
- No Runtime —— 无运行时代码
- Less-Code —— 写更少的代码
- Hight-Performance —— 高性能
Svelte 劣势
- 社区
- 社区
- 社区
原理概览
Svelte 在编译时,就已经分析好了数据 和 DOM 节点之间的对应关系,在数据发生变化时,可以非常高效的来更新DOM节点。
- Rich Harris 在进行Svelte的设计的时候没有采用 Virtual DOM,主要是因为他觉得Virtual DOM Diff 的过程是非常低效的。具体可参考Virtual Dom 真的高效吗[21]一文;Svelte 采用了Templates语法,在编译的过程中就进行优化操作;
- Svelte 记录脏数据的方式:位掩码(bitMask);
- 数据和DOM节点之间的对应关系:React 和 Vue 是通过 Virtual Dom 进行 diff 来算出来更新哪些 DOM 节点效率最高。Svelte 是在编译时候,就记录了数据 和 DOM 节点之间的对应关系,并且保存在 p 函数中。
写在最后
我是零一,如果我的文章对你有帮助,请点个 赞👍🏻 支持我一下