初探React源码—起手式

虚拟 DOM

Virtual DOM 是一种编程概念。在这个概念里, UI 以一种理想化的,或者说"虚拟的"表现形式被保存于内存中,并通过如 ReactDOM 等类库使之与"真实的" DOM 同步。这一过程叫做协调

虚拟DOM可以看做一棵模拟了DOM树的JavaScript对象树。

css 复制代码
const element = {
    element: 'ul',
    props: {
        id:"ulist"
    },
    children: [
    { element: 'li', props: { id:"first" }, children: ['这是第一个List元素'] },
    { element: 'li', props: { id:"second" }, children: ['这是第二个List元素'] }
    ]
}

为什么使用虚拟DOM

在传统的 Web 应用中,我们往往会把数据的变化实时地更新到用户界面中,于是每次数据的微小变动都会引起 DOM 树的重新渲染。

虚拟DOM的目的是将所有操作累加起来,统计计算出所有的变化后,统一更新一次DOM。

why?

DOM操作很慢,轻微的操作都可能导致页面重新排版,非常耗性能。相对于DOM对象,js 对象处理起来更快,而且更简单。通过diff算法对比新旧vdom之间的差异,可以批量的、最小化的执行dom操作,从而提升用户体。

协调

设计动力

在某一时间节点调用React的render() 方法,会创建一棵由React元素组成的树。在下一次state或props更新时,相同的render() 方法会返回一棵不同的树。React需要基于这两棵树之间的差别来判断如何有效率的更新UI以保证当前UI与最新的树保持同步。

这个算法问题有一些通用的解决方案,即生成将一棵树转换成另一 棵树的最小操作数。然而,即使在最前沿的算法中,该算法的复杂程度为 O(n3),其中 n是树中元素的数量。

如果在React中使用了该算法,那么展示1000个元素所需要执行的计算量将在十亿的量级范围。这个开销实在是太过高昂。于是React在以下两个假设的基础之上提出了一套 O(n)的启发式算法:

1.两个不同类型的元素会产生出不同的树;

2.开发者可以通过key prop 来暗示哪些子元素在不同力染下能保持稳定;

算法复杂度O(n)

diff 策略

  1. 同级比较,Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计。如出现跨层级的移动操做,则直接将原 DOM 树删除,重新创建

所以下图的 D 节点所在 DOM 树会被删除重新创建,F 节点也会被删除后,重新创建

  1. 拥有不同类型的两个组件将会生成不同的树形结构。不同类型的两个组件则认为是删除原 DOM 树之后重新创建

下图的 D 节点所在 DOM 树会被删除后重新创建

  1. 开发者可以通过 key prop 来暗示哪些子元素在不同的渲染下能保持稳定

不使用 key 或使用 index 作为 key 时,新增 F 节点,下图会进行 F、B、C、D 节点的更新,并新增 E 节点

使用唯一 key 值时,只会新增 F 节点添加到 B 节点之前,B、C、D、E 节点都不会发生更新操作

fiber 思想

将树形结构转化成链表结构

diff过程

比对两个虚拟dom时会有三种操作:删除、替换和更新

vnode是现在的虚拟dom, newVnode 是新虚拟dom。

  1. 删除: newVnode 不存在时
  2. 替换: vnode 和newVnode类型不同或key不同时
  3. 更新:有相同类型和key但vnode和newVnode不同时

在实践中也证明这三个前提策略是合理且准确的,它保证了整体界面构建的性能。

渲染过程

fiber 渲染过程

之前的树形结构渲染过程:

Fiber

github.com/acdlite/rea...

本质上是对 vdom 的操作

1.为什么需要fiber

对于大型项目,组件树会很大,这个时候递归遍历的成本就会很高,会造成主线程被持续占用,结果就是主线程上的布局、动画等周期性任务就无法立即得到处理,造成视觉上的卡顿,影响用户体验。

2.任务分解的意义

解决上面的问题

3.增量渲染(把渲染任务拆分成块,匀到多帧)

4.更新时能够暂停,终止,复用渲染任务

5.给不同类型的更新赋予优先级

6.并发方面新的基础能力

位运算

flags定义为二进制,而不是字符串或者单个数字,一方面原因是因为二进制单个数字具有唯一性、某个范围内的组合

同样具有唯一性,另一方原因在于简洁方便、且速度快。

window.requestldleCalback()

方法将在浏览器的空闲时段内调用的函数排队。 这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。函数 一 般会按先进先调用的顺序执行,然而,如果回调函数指定了执行超时时间timeout ,则有可能为了在超时前执行函数而打乱执行顺序。

你可以在空闲回调函数中调用requestIdleCallback() ,以便在下一次通过事件循环之前调度另一 个回调。

callback

一个在事件循环空闲时即将被调用的函数的引用。函数会接收到一个名为IdleDeadline 的参数,这个参数可以获取当前空闲时间以及回调是否在超时时间前已经执行的状态。

options可选

包括可选的配置参数。具有如下属性:

timeout:如果指定了timeout并具有一个正值,并且尚未通过超时毫秒数调用回调,那么回调会在下一次空闲时期被强制执行,尽管这样很可能会对性能造成负面影响。

FiberReact 16中新的协调引擎。它的主要目的是使Virtual DOM可以进行增量式渲染。

一个更新过程可能被打断,所以React Fiber一个更新过程被分为两个阶段:

第一个阶段Reconciliation Phase

和第二阶段Commit Phase

相关推荐
万叶学编程2 小时前
Day02-JavaScript-Vue
前端·javascript·vue.js
前端李易安4 小时前
Web常见的攻击方式及防御方法
前端
PythonFun4 小时前
Python技巧:如何避免数据输入类型错误
前端·python
知否技术4 小时前
为什么nodejs成为后端开发者的新宠?
前端·后端·node.js
hakesashou4 小时前
python交互式命令时如何清除
java·前端·python
天涯学馆4 小时前
Next.js与NextAuth:身份验证实践
前端·javascript·next.js
HEX9CF5 小时前
【CTF Web】Pikachu xss之href输出 Writeup(GET请求+反射型XSS+javascript:伪协议绕过)
开发语言·前端·javascript·安全·网络安全·ecmascript·xss
ConardLi5 小时前
Chrome:新的滚动捕捉事件助你实现更丝滑的动画效果!
前端·javascript·浏览器
ConardLi5 小时前
安全赋值运算符,新的 JavaScript 提案让你告别 trycatch !
前端·javascript