React Fiber 是Facebook破费两年余工夫对 React 做出的一个重大扭转与优化,是对 React 外围算法的一次从新实现。从Facebook在 React Conf 2017会议上确认,React Fiber 会在React 16 版本公布至今,也已过来三年无余,现在,React 17 业已公布,社区对于Fiber的优良文章不在少数。
本文源于一次团队外部的技术分享,借鉴社区优良文章,联合集体了解,进行整合,从六个问题登程,对 React Fiber 进行了解与意识,同时对时下热门的前端框架Svelte进行简要介绍与分析,心愿对正在探索 React 及各前端框架的小伙伴们能有所助益。
全文大量参考和援用以下几篇博文,读者可自行查阅:
- React技术揭秘
- 前端工程师的自我涵养:React Fiber 是如何实现更新过程可控的
- 新兴前端框架 Svelte 从入门到原理
- 以 React 为例,说说框架和性能(下)
一、React 的设计理念是什么?
React官网在React哲学一节开篇提到:
咱们认为,React 是用 JavaScript 构建疾速响应的大型 Web 应用程序的首选形式。它在 Facebook 和 Instagram 上体现优良。React 最棒的局部之一是疏导咱们思考如何构建一个利用。
由此可见,React 谋求的是 “疾速响应”,那么,“疾速响应“的制约因素都有什么呢?
- CPU的瓶颈:当我的项目变得宏大、组件数量繁多、遇到大计算量的操作或者设施性能有余使得页面掉帧,导致卡顿。
- IO的瓶颈:发送网络申请后,因为须要期待数据返回能力进一步操作导致不能疾速响应。
本文要聊的fiber 架构次要就是用来解决 CPU 和网络的问题,这两个问题始终也是最影响前端开发体验的中央,一个会造成卡顿,一个会造成白屏。为此 react 为前端引入了两个新概念:Time Slicing 工夫分片和Suspense。
二、React的“先天不足” —— 据说 Vue 3.0 采纳了动静联合的 Dom diff,React 为何不跟进?
Vue 3.0 动静联合的 Dom diff
Vue3.0 提出动静联合的 DOM diff 思维,动静联合的 DOM diff其实是在预编译阶段进行了优化。之所以可能做到预编译优化,是因为 Vue core 能够动态剖析 template,在解析模版时,整个 parse 的过程是利用正则表达式程序解析模板,当解析到开始标签、闭合标签和文本的时候都会别离执行对应的回调函数,来达到结构 AST 树的目标。
借助预编译过程,Vue 能够做到的预编译优化就很弱小了。比方在预编译时标记出模版中可能变动的组件节点,再次进行渲染前 diff 时就能够跳过“永远不会变动的节点”,而只须要比照“可能会变动的动静节点”。这也就是动静联合的 DOM diff 将 diff 老本与模版大小正相干优化到与动静节点正相干的理论依据。
React 是否像 Vue 那样进行预编译优化?
Vue 须要做数据双向绑定,须要进行数据拦挡或代理,那它就须要在预编译阶段动态剖析模版,剖析出视图依赖了哪些数据,进行响应式解决。而 React 就是部分从新渲染,React 拿到的或者说主持的,所负责的就是一堆递归 React.createElement 的执行调用(参考下方通过Babel转换的代码),它无奈从模版层面进行动态剖析。JSX 和手写的 render function 是齐全动静的,适度的灵活性导致运行时能够用于优化的信息有余。
JSX 写法:
<div> <h1>六个问题助你了解 React Fiber</h1> <ul> <li>React</li> <li>Vue</li> </ul></div>
递归 React.createElement:
// Babel转换后React.createElement( "div", null, React.createElement( "h1", null, "\u516D\u4E2A\u95EE\u9898\u52A9\u4F60\u7406\u89E3 React Fiber" ), React.createElement( "ul", null, React.createElement("li", null, "React"), React.createElement("li", null, "Vue") ));
JSX vs Template
- JSX 具备 JavaScript 的残缺表现力,能够构建非常复杂的组件。然而灵便的语法,也意味着引擎难以了解,无奈预判开发者的用户用意,从而难以优化性能。
- Template 模板是一种十分有束缚的语言,你只能以某种形式去编写模板。
既然存在以上编译时先天不足,在运行时优化方面,React始终在致力。比方,React15实现了batchedUpdates(批量更新)。即同一事件回调函数上下文中的屡次setState只会触发一次更新。
然而,如果单次更新就很耗时,页面还是会卡顿(这在一个保护工夫很长的大利用中是很常见的)。这是因为React15的更新流程是同步执行的,一旦开始更新直到页面渲染前都不能中断。
材料参考:以 React 为例,说说框架和性能(下) | 新兴前端框架 Svelte 从入门到原理
三、从架构演变看一直进击的 React 都做过哪些优化?
React渲染页面的两个阶段
- 调度阶段(reconciliation):在这个阶段 React 会更新数据生成新的 Virtual DOM,而后通过Diff算法,疾速找出须要更新的元素,放到更新队列中去,失去新的更新队列。
- 渲染阶段(commit):这个阶段 React 会遍历更新队列,将其所有的变更一次性更新到DOM上。
React 15 架构
React15架构能够分为两层:
- Reconciler(协调器)—— 负责找出变动的组件;
- Renderer(渲染器)—— 负责将变动的组件渲染到页面上;
在React15及以前,Reconciler采纳递归的形式创立虚构DOM,递归过程是不能中断的。如果组件树的层级很深,递归会占用线程很多工夫,递归更新工夫超过了16ms,用户交互就会卡顿。
为了解决这个问题,React16将递归的无奈中断的更新重构为异步的可中断更新,因为已经用于递归的虚构DOM数据结构曾经无奈满足需要。于是,全新的Fiber架构应运而生。
React 16 架构
为了解决同步更新长时间占用线程导致页面卡顿的问题,也为了摸索运行时优化的更多可能,React开始重构并始终继续至今。重构的指标是实现Concurrent Mode(并发模式)。
从v15到v16,React团队花了两年工夫将源码架构中的Stack Reconciler重构为Fiber Reconciler。
React16架构能够分为三层:
- Scheduler(调度器)—— 调度工作的优先级,高优工作优先进入Reconciler;
- Reconciler(协调器)—— 负责找出变动的组件:更新工作从递归变成了能够中断的循环过程。Reconciler外部采纳了Fiber的架构;
- Renderer(渲染器)—— 负责将变动的组件渲染到页面上。
React 17 优化
React16的expirationTimes模型只能辨别是否>=expirationTimes
决定节点是否更新。React17的lanes模型能够选定一个更新区间,并且动静的向区间中增减优先级,能够解决更细粒度的更新。
Lane用二进制位示意工作的优先级,不便优先级的计算(位运算),不同优先级占用不同地位的“赛道”,而且存在批的概念,优先级越低,“赛道”越多。高优先级打断低优先级,新建的工作须要赋予什么优先级等问题都是Lane所要解决的问题。
Concurrent Mode的目标是实现一套可中断/复原的更新机制。其由两局部组成:
- 一套协程架构:Fiber Reconciler
- 基于协程架构的启发式更新算法:管制协程架构工作形式的算法
材料参考:React17新个性:启发式更新算法
四、浏览器一帧都会干些什么以及requestIdleCallback的启发
浏览器一帧都会干些什么?
咱们都晓得,页面的内容都是一帧一帧绘制进去的,浏览器刷新率代表浏览器一秒绘制多少帧。原则上说 1s 内绘制的帧数也多,画面体现就也细腻。目前浏览器大多是 60Hz(60帧/s),每一帧耗时也就是在 16.6ms 左右。那么在这一帧的(16.6ms) 过程中浏览器又干了些什么呢?
通过下面这张图能够分明的晓得,浏览器一帧会通过上面这几个过程:
- 承受输出事件
- 执行事件回调
- 开始一帧
- 执行 RAF (RequestAnimationFrame)
- 页面布局,款式计算
- 绘制渲染
- 执行 RIC (RequestIdelCallback)
第七步的 RIC 事件不是每一帧完结都会执行,只有在一帧的 16.6ms 中做完了后面 6 件事儿且还有剩余时间,才会执行。如果一帧执行完结后还有工夫执行 RIC 事件,那么下一帧须要在事件执行完结能力持续渲染,所以 RIC 执行不要超过 30ms,如果长时间不将控制权交还给浏览器,会影响下一帧的渲染,导致页面呈现卡顿和事件响应不及时。
requestIdleCallback 的启发
咱们以浏览器是否有剩余时间作为工作中断的规范,那么咱们须要一种机制,当浏览器有剩余时间时告诉咱们。
requestIdleCallback((deadline) => {// deadline 有两个参数 // timeRemaining(): 以后帧还剩下多少工夫 // didTimeout: 是否超时// 另外 requestIdleCallback 后如果跟上第二个参数 {timeout: ...} 则会强制浏览器在以后帧执行完后执行。 if (deadline.timeRemaining() > 0) { // TODO } else { requestIdleCallback(otherTasks); }});
// 用法示例var tasksNum = 10000requestIdleCallback(unImportWork)function unImportWork(deadline) { while (deadline.timeRemaining() && tasksNum > 0) { console.log(`执行了${10000 - tasksNum + 1}个工作`) tasksNum-- } if (tasksNum > 0) { // 在将来的帧中继续执行 requestIdleCallback(unImportWork) }}
其实局部浏览器曾经实现了这个API,这就是requestIdleCallback。然而因为以下因素,Facebook 摈弃了 requestIdleCallback 的原生 API:
- 浏览器兼容性;
- 触发频率不稳固,受很多因素影响。比方当咱们的浏览器切换tab后,之前tab注册的requestIdleCallback触发的频率会变得很低。
参考: requestIdleCallback 的 FPS 只有 20
基于以上起因,在React中实现了性能更齐备的requestIdleCallbackpolyfill,这就是Scheduler。除了在闲暇时触发回调的性能外,Scheduler还提供了多种调度优先级供工作设置。
材料参考:requestIdleCallback-后台任务调度
五、 Fiber 为什么是 React 性能的一个飞跃?
什么是 Fiber
Fiber 的英文含意是“纤维”,它是比线程(Thread)更细的线,比线程(Thread)管制得更精细的执行模型。在狭义计算机科学概念中,Fiber 又是一种合作的(Cooperative)编程模型(协程),帮忙开发者用一种【既模块化又合作化】的形式来编排代码。
在 React 中,Fiber 就是 React 16 实现的一套新的更新机制,让 React 的更新过程变得可控,防止了之前采纳递归须要零打碎敲影响性能的做法。
React Fiber 中的工夫分片
把一个耗时长的工作分成很多小片,每一个小片的运行工夫很短,尽管总工夫仍然很长,然而在每个小片执行完之后,都给其余工作一个执行的机会,这样惟一的线程就不会被独占,其余工作仍然有运行的机会。
React Fiber 把更新过程碎片化,每执行完一段更新过程,就把控制权交还给 React 负责工作协调的模块,看看有没有其余紧急任务要做,如果没有就持续去更新,如果有紧急任务,那就去做紧急任务。
Stack Reconciler
基于栈的 Reconciler,浏览器引擎会从执行栈的顶端开始执行,执行结束就弹出以后执行上下文,开始执行下一个函数,直到执行栈被清空才会进行。而后将执行权交还给浏览器。因为 React 将页面视图视作一个个函数执行的后果。每一个页面往往由多个视图组成,这就意味着多个函数的调用。
如果一个页面足够简单,造成的函数调用栈就会很深。每一次更新,执行栈须要一次性执行实现,中途不能干其余的事儿,只能"全心全意"。联合后面提到的浏览器刷新率,JS 始终执行,浏览器得不到控制权,就不能及时开始下一帧的绘制。如果这个工夫超过 16ms,当页面有动画成果需要时,动画因为浏览器不能及时绘制下一帧,这时动画就会呈现卡顿。不仅如此,因为事件响应代码是在每一帧开始的时候执行,如果不能及时绘制下一帧,事件响应也会提早。
Fiber Reconciler
链表构造
在 React Fiber 中用链表遍历的形式代替了 React 16 之前的栈递归计划。在 React 16 中应用了大量的链表。
- 应用多向链表的模式代替了原来的树结构;
<div id="A"> A1 <div id="B1"> B1 <div id="C1"></div> </div> <div id="B2"> B2 </div></div>
- 副作用单链表;
- 状态更新单链表;
- ...
链表是一种简略高效的数据结构,它在以后节点中保留着指向下一个节点的指针;遍历的时候,通过操作指针找到下一个元素。
链表相比程序构造数据格式的益处就是:
- 操作更高效,比方程序调整、删除,只须要扭转节点的指针指向就好了。
- 不仅能够依据以后节点找到下一个节点,在多向链表中,还能够找到他的父节点或者兄弟节点。
但链表也不是完满的,毛病就是:
- 比程序构造数据更占用空间,因为每个节点对象还保留有指向下一个对象的指针。
- 不能自在读取,必须找到他的上一个节点。
React 用空间换工夫,更高效的操作能够不便依据优先级进行操作。同时能够依据以后节点找到其余节点,在上面提到的挂起和复原过程中起到了关键作用。
斐波那契数列的 Fiber
递归模式的斐波那契数列写法:
function fib(n) { if (n <= 2) { return 1; } else { return fib(n - 1) + fib(n - 2); }}
采纳 Fiber 的思路将其改写为循环(这个例子并不能和 React Fiber 的对等):
function fib(n) { let fiber = { arg: n, returnAddr: null, a: 0 }, consoled = false; // 标记循环 rec: while (true) { // 当开展齐全后,开始计算 if (fiber.arg <= 2) { let sum = 1; // 寻找父级 while (fiber.returnAddr) { if(!consoled) { // 在这里打印查看造成的链表模式的 fiber 对象 consoled=true console.log(fiber) } fiber = fiber.returnAddr; if (fiber.a === 0) { fiber.a = sum; fiber = { arg: fiber.arg - 2, returnAddr: fiber, a: 0 }; continue rec; } sum += fiber.a; } return sum; } else { // 先开展 fiber = { arg: fiber.arg - 1, returnAddr: fiber, a: 0 }; } }}
六、React Fiber 是如何实现更新过程可控?
更新过程的可控次要体现在上面几个方面:
- 工作拆分
- 工作挂起、复原、终止
- 工作具备优先级
工作拆分
在 React Fiber 机制中,它采纳"化整为零"的思维,将和谐阶段(Reconciler)递归遍历 VDOM 这个大工作分成若干小工作,每个工作只负责一个节点的解决。
工作挂起、复原、终止
workInProgress tree
workInProgress 代表以后正在执行更新的 Fiber 树。在 render 或者 setState 后,会构建一颗 Fiber 树,也就是 workInProgress tree,这棵树在构建每一个节点的时候会收集以后节点的副作用,整棵树构建实现后,会造成一条残缺的副作用链。
currentFiber tree
currentFiber 示意上次渲染构建的 Filber 树。在每一次更新实现后 workInProgress 会赋值给 currentFiber。在新一轮更新时 workInProgress tree 再从新构建,新 workInProgress 的节点通过 alternate 属性和 currentFiber 的节点建立联系。
在新 workInProgress tree 的创立过程中,会同 currentFiber 的对应节点进行 Diff 比拟,收集副作用。同时也会复用和 currentFiber 对应的节点对象,缩小新创建对象带来的开销。也就是说无论是创立还是更新、挂起、复原以及终止操作都是产生在 workInProgress tree 创立过程中的。workInProgress tree 构建过程其实就是循环的执行工作和创立下一个工作。
挂起
当第一个小工作实现后,先判断这一帧是否还有闲暇工夫,没有就挂起下一个工作的执行,记住以后挂起的节点,让出控制权给浏览器执行更高优先级的工作。
复原
在浏览器渲染完一帧后,判断以后帧是否有剩余时间,如果有就复原执行之前挂起的工作。如果没有工作须要解决,代表和谐阶段实现,能够开始进入渲染阶段。
- 如何判断一帧是否有闲暇工夫的呢?
应用后面提到的 RIC (RequestIdleCallback) 浏览器原生 API,React 源码中为了兼容低版本的浏览器,对该办法进行了 Polyfill。
- 复原执行的时候又是如何晓得下一个工作是什么呢?
答案是在后面提到的链表。在 React Fiber 中每个工作其实就是在解决一个 FiberNode 对象,而后又生成下一个工作须要解决的 FiberNode。
终止
其实并不是每次更新都会走到提交阶段。当在和谐过程中触发了新的更新,在执行下一个工作的时候,判断是否有优先级更高的执行工作,如果有就终止原来将要执行的工作,开始新的 workInProgressFiber 树构建过程,开始新的更新流程。这样能够防止反复更新操作。这也是在 React 16 当前生命周期函数 componentWillMount 有可能会执行屡次的起因。
工作具备优先级
React Fiber 除了通过挂起,复原和终止来管制更新外,还给每个任务分配了优先级。具体点就是在创立或者更新 FiberNode 的时候,通过算法给每个任务分配一个到期工夫(expirationTime)。在每个工作执行的时候除了判断剩余时间,如果以后解决节点曾经过期,那么无论当初是否有闲暇工夫都必须执行该工作。过期工夫的大小还代表着工作的优先级。
工作在执行过程中顺便收集了每个 FiberNode 的副作用,将有副作用的节点通过 firstEffect、lastEffect、nextEffect 造成一条副作用单链表 A1(TEXT)-B1(TEXT)-C1(TEXT)-C1-C2(TEXT)-C2-B1-B2(TEXT)-B2-A。
其实最终都是为了收集到这条副作用链表,有了它,在接下来的渲染阶段就通过遍历副作用链实现 DOM 更新。这里须要留神,更新实在 DOM 的这个动作是零打碎敲的,不能中断,不然会造成视觉上的不连贯(commit)。
<div id="A1"> A1 <div id="B1"> B1 <div id="C1">C1</div> <div id="C2">C2</div> </div> <div id="B2"> B2 </div></div>
直观展现
正是基于以上这些过程,应用Fiber,咱们就有了在社区常常看到的两张比照图。
清晰展现及交互、源码可通过上面两个链接进入,查看网页源代码。
- Stack Example
- Fiber Example
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 树之间的互相援用 }}
图片起源:齐全了解React Fiber
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:Suspense 是 React 提供的一种异步解决的机制, 它不是一个具体的数据申请库。它是React 提供的原生的组件异步调用原语。
- useTrasition:让页面实现
Pending -> Skeleton -> Complete
的更新门路, 用户在切换页面时能够停留在以后页面,让页面放弃响应。 相比展现一个无用的空白页面或者加载状态,这种用户体验更加敌对。
其中 Suspense 能够用来解决申请阻塞的问题,UI 卡顿的问题其实开启 concurrent mode 就曾经解决的,但如何利用 concurrent mode 来实现更敌对的交互还是须要对代码做一番改变的。
材料参考:Concurrent 模式介绍 (实验性) | 了解 React Fiber & Concurrent Mode | 11.concurrent mode(并发模式是什么样的) | 人人都能读懂的react源码解析
将来可期
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 版本开始提供,其余浏览器并未实现。
材料参考:Facebook 将对 React 的优化实现到了浏览器! | Faster input events with Facebook’s first browser API contribution
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 真的高效吗一文;Svelte 采纳了Templates语法,在编译的过程中就进行优化操作;
- Svelte 记录脏数据的形式:位掩码(bitMask);
- 数据和DOM节点之间的对应关系:React 和 Vue 是通过 Virtual Dom 进行 diff 来算进去更新哪些 DOM 节点效率最高。Svelte 是在编译时候,就记录了数据 和 DOM 节点之间的对应关系,并且保留在 p 函数中。
材料参考:新兴前端框架 Svelte 从入门到原理
材料参考与举荐
- React技术揭秘
- 前端工程师的自我涵养:React Fiber 是如何实现更新过程可控的
- 新兴前端框架 Svelte 从入门到原理
- 以 React 为例,说说框架和性能(下)