学习二十七、Virtual DOM 以及 Diff算法

JSX是什么

它是 javascript 语法的扩展, React 使用它来描述用户界面长成什么样子。虽然它看起来非常像HTML,但它确实是 javascript。在React 代码执行之前,Babel 会将JSX 编译 React API。

Virtual DOM

在 React 中,每个 DOM 对象都有一个对应的 Virtual DOM 对象,它是 DOM 对象的 javascript 对象表现形式其实就是使用 javascript 对象来描述 DOM 对象信息,比如 DOM 对象的类型是什么,它的身上有哪些属性,它拥有哪些子元素。

可以把 Virtual DOM 对象理解为 DOM 对象的副本,但是他不能直接显示在屏幕上。

Virtual DOM 如何提升效率
精准找出发生变化的 DOM 对象,值更新发生变化的部分。

在 React 第一次创建 DOM 对象后,会为每个 DOM 对象创建期对应的 Virtual DOM 对象,在 DOM 对象发生更新之前, React 会更新所有的 Virtual DOM 对象, 然后 React 会将更新后的 Virtual DOM 和更新之前的 Virtual DOM 进行对比,从而找出发生变化的部分,React 会将发生变化的部分更新到真实的 DOM 对象中, React 仅更新必要更新的部分。

Virtual DOM 对象的更新和比较仅发生在内存中,不会在视图中渲染任何内容,所以这一部分的性能损耗成本是微不足道的。

创建 Virtual DOM 对象

在 React 代码执行之前,JSX 会被 Babel 转换为 React.createElement 方法调用,在调用 createElement 方法时会闯入元素的类型,元素的属性,以及元素的子元素,createElement方法返回值为构建的 Virtual DOM 对象。

组件渲染之区分函数组件和类组件

在渲染组件之前首先要明确的是,组件的 Virtual DOM 类型值为函数,函数组件和类组件都是这样的。

// 原始组件
const Heart = () => <span>&hearts;<span>
<Heart />
// 组件的 Virtual DOM
{
    
    
  type: f function() {
    
    };
  props: {
    
    }
  children: []
}

Virtual DOM 比对

深度递归遍历比对更新节点信息

删除节点

删除节点发生在节点更新以后并且发生在同一个父节点下的所有子节点身上。

在节点更新完成之后,如果旧节点对象的数量多于新节点 VirtualDOM 节点的数量,就说明有节点需要被删除。

setState 方法实现组件更新

在 diff 方法中判断要更新的 Virtual DOM 是否是组件

如果是组件再判断要更新的组件和未更新前的组件是否是同一个组件,如果不是用一个组件就不需要做组件更新操作,直接调用 mountElement 方法将返回的 Virtual DOM 添加到页面中

如果是同一个组件,就执行更新组件的操作,其实就是将最新的 props 传递到组件中,在调用组件的 render 方法获取组件返回的最新的 Virtual DOM 对象,再将 Virtual DOM 对象传递给 diff 方法,让 diff 方法找出差异,从而将差异更新到真实 DOM 对象中。

在更新组件的过程中还要在不同阶段调用不同的生命周期函数。

在 diff 方法中判断要更新的 Virtual DOM 是否是组件,如果是组件又分为多种情况,新增 diffComponent 方法进行处理

实现 ref 属性获取元素 DOM 对象获取组件实例对象

为节点添加 ref 属性可以获取到这个节点的 DOM 对象,比如在 DemoRef 类中,为 input 元素添加了 ref 属性,目的是获取 input DOM 元素对象,在点击按钮时获取用户在文本框中的内容

实现思路是在创建节点时判断其 Virtual DOM 对象中是否含有 ref 属性,如果有就调用 ref 属性中所存储的方法并且将创建出来的DOM对象作为参数传递给 ref 方法,这样在渲染组件节点的时候就可以拿到元素对象并将元素对象存储为组件属性。

在类组件的身上可以添加 ref 属性,目的是获取组件的实例对象。

使用 key 属性进行节点对比

在 React 中,渲染列表数据时通常会被渲染的列表元素上添加 key 属性,key 属性就是数据的唯一标识,帮助 React 识别哪些数据被修改或者删除了,从而达到 DOM 最小化操作的目的。

key 属性不需要全局唯一,但是在同一个父节点下的兄弟节点之间必须是唯一的。

也就是说,在比对同一个父节点下类型相同的子节点是需要使用到 key 属性。

节点对比

实现思路是在两个元素进行比对时,如果类型相同,就循环旧的DOM对象的子元素,查看其身上是否有 key 属性,如果有就将这个子元素的 DOM 对象存储在一个 javascript 对象中,接着循环要渲染的 Virtual DOM 对象的子元素,在循环过程中获取到这个子元素的 key 属性,然后使用这个 key 属性到 Javascript 对象中查找 DOM 对象,如果能够找到就说明这个元素是已经存在的,是不需要重新渲染的,如果通过 key 属性找不到这个元素,就说明这个元素是新增的是需要渲染的。

删除节点

在比对节点的过程中,如果节点的数量多于要渲染的新节点的数量就说明有节点被删除,继续判断keyedElements 对象中是否含有元素,如果没有就使用索引方式删除,如果有就使用 key 属性比对的方式进行删除。

实现思路是循环节点,在循环旧节点的过程中获取旧节点对应的 key 属性,然后根据 key 属性所在新节点中查找这个旧节点,如果找到旧说明这个节点没有被删除,如果没有找到,说明这个节点被删除了,调用卸载节点的方法卸载节点即可。

删除节点并不是说将节点直接删除就可以了,还需要考虑以下几种情况:

如果要删除的节点是文本节点的话可以直接删除
如果要删除的节点由组件组成,需要调用组件卸载生命周期的函数
如果要删除的节点中包含了其他组件生成的节点,需要调用其他组件的卸载生命周期函数
如果要删除的节点身上由 ref 属性,还需要删除通过 ref 属性传递给组件的 DOM 节点对象
如果删除的节点身上由事件,需要删除事件对应的事件处理函数

requestIdleCallback

核心 API 功能介绍

利用浏览器的空余时间执行任务,如果有更高优先级的任务要执行时,当前执行的任务可以被终止,优先执行高级别任务。

浏览器空余时间

页面是一帧一帧测绘出来的,当每秒绘制的帧数达到60时,页面是流畅的,小于这个值时,用户会感觉到卡顿
60帧/秒,每一帧分到的时间时 1000/60 约等于 16ms, 如果每一帧执行的时间小于 16ms,就说明浏览器有空余时间
如果浏览器在剩余时间内没有完成则会停止任务执行,优先执行主任务,也就是说 requestIdleCallback 总是利用浏览器的空余时间执行任务

猜你喜欢

转载自blog.csdn.net/qq_40289624/article/details/113185642
今日推荐