React学习20220314 虚拟DOM

React学习20220314 虚拟DOM

虚拟DOM是什么

是JS和DOM之间的一个映射缓存,在形态上表现为一个能够描述DOM结构及其属性信息的JS对象。

在REACT中,表现为

image-20220314142105501

是JS对象,是对真实DOM的描述

如何工作

  • 挂载阶段:结合JSX的描述,构建出虚拟DOM树,然后通过ReactDOM.render实现虚拟DOM到真实DOM的映射
  • 更新阶段,页面的变化在作用于真实DOM之前,先作用于虚拟DOM,虚拟DOM在JS层借助算法先对比出那些真实DOM需要被改变,然后将改变作用于真实DOM

这一段历史讲得好有趣,摘下来

image-20220314142856250

image-20220314145559439

image-20220314145616729

可以看出,模板语法其实就是把 JS 和 HTML 结合在一起的一种规则,而模板引擎做的事情也非常容易理解。

把 staff 这个数据源读进去,塞到预置好的 HTML 模板里,然后把两者融合在一起,吐出一段目标字符串给你。这段字符串的内容,其实就是一份标准的、可用于渲染的 HTML 代码,它将对应一个 DOM 元素。最后,将这个 DOM 元素挂载到页面中去,整个模板的渲染流程也就走完了。

这个过程可以用伪代码来表示,如下所示:

// 数据和模板融合出 HTML 代码
var targetDOM = template({data: students})
// 添加到页面中去
document.body.appendChild(targetDOM)

当然,实际的过程会比我们描述的要复杂一些。这里我补充一下模板引擎的实现思路,供感兴趣的同学参考。模板引擎一般需要做下面几件事情:

  1. 读取 HTML 模板并解析它,分离出其中的 JS 信息;

  2. 将解析出的内容拼接成字符串,动态生成 JS 代码;

  3. 运行动态生成的 JS 代码,吐出“目标 HTML”;

  4. 将“目标 HTML”赋值给 innerHTML,触发渲染流水线,完成真实 DOM 的渲染。

使用模板引擎方案来渲染数据是非常爽的:每次数据发生变化时,我们都不用关心到底是哪里的数据变了,也不用手动去点对点完成 DOM 的修改。只需要关注的仅仅是数据和数据变化本身,DOM 层面的改变模板引擎会帮我们做掉。

如此看来,模板引擎像极了一个只需要接收命令,就能够把活干得漂漂亮亮的“扫地机器人”!可惜的是,模板引擎出现的契机虽然是为了使用户界面与业务数据相分离,但实际的应用场景基本局限在“实现高效的字符串拼接”这一个点上,因此不能指望它去做太复杂的事情。尤其令人无法接受的是,它在性能上的表现并不尽如人意:由于不够“智能”,它更新 DOM 的方式是将已经渲染出 DOM 整体注销后再整体重渲染,并且不存在更新缓冲这一说。在 DOM 操作频繁的场景下,模板引擎可能会直接导致页面卡死。

注:请注意小标题中“早期”这个限定词——本课时所讨论的“模板引擎”概念,指的是虚拟 DOM 思想推而广之以前,相对原始的一类模板引擎,这类模板引擎曾经主导了一个时代。但时下来看,越来越多的模板引擎正在引入虚拟 DOM,模板引擎最终也将走向现代化。

虽然指望模板引擎实现生产力解放有些天方夜谭,但模板引擎在思想上无疑具备高度的先进性:允许程序员只关心数据而不必关心 DOM 细节的这一操作,和 React 的“数据驱动视图”思想如出一辙,实在是高!

那该怎么办呢?

jQuery 救不了加班写 DOM 操作的前端,模板引擎也救不了,那该怎么办呢?

这时候有一批仁人志士,兴许是从模板引擎的设计思想上得到了启发,他们明确了要走“数据驱动视图”这条基本道路,于是便沿着这个思路往下摸索:模板引擎的数据驱动视图方案,核心问题在于对真实 DOM 的修改过于“大刀阔斧”,导致了 DOM 操作的范围过大、频率过高,进而可能会导致糟糕的性能。然后这帮人就想啊:既然操作真实 DOM 对性能损耗这么大,那我操作假的 DOM 不就行了?

沿着这个思路再往下走,就有了我们都爱的虚拟 DOM。

注:出于严谨,还是要解释下。真实历史中的虚拟 DOM 创作过程,到底有没有向模板引擎去学习,这个暂时无从考证。但是按照前端发展的过程来看,模板引擎和虚拟 DOM 确实在思想上存在递进关系,很多场景下,面试官也可能会问及两者的关系。因此在此处,我采取了这样一种表述方式,希望能够帮助你更好地把握住问题的关键所在。

虚拟DOM使得之前的全局刷新,改成了有更新的部分更新,在真实DOM前加了一层

image-20220314151706530

虚拟DOM解决的问题重心不是性能

image-20220314151854443

Reconciliation过程与Diff算法

Diff算法

找两个树结构之间的不同

两个规律

image-20220314152249841

image-20220314153544145

对于Key,下图可以比较好地展示使用了Key后的更新策略

image-20220314153745501

如果不使用key,则仅有AB可保留,其他均被重建

React学习20220314 SetState

React学习20220314 SetState

初始认知

setState之后立马访问对应state,会发现它并没有改变,而是会在之后某个时间发生变化

异步的动机和原理

图片3.png

异步避免重复reRender

image-20220314185248371

从源码角度看异步setState

image-20220314185446537

再查看batchingStrategy

image-20220314185810854

理解Transaction(事务)机制

image-20220314185913490

同步现象的本质

image-20220314190033221

React学习20220313 Hook

React学习20220313 Hook

理解Hook

按我的理解,Hook是对繁琐,学习曲线长,难以更改中间步骤的类的拆分,使得函数可以拥有一些类能够使用的功能:如state和生命周期等,而且Hook所辅助的函数式编程比较符合React所推崇的组件化编程。

对useState的理解

充当函数中的state,但一次只有一个,不像类中一次指定多个

[text,setText] =useState(‘初始文字’)

useState返回一个数组,数组第一个是想要的state变量,第二个是修改变量的Api

对useEffect的理解

弥补生命周期

接收两个参数:回调函数和依赖数组

useEffect(callBack,[])

调用规则:

每次渲染后:传入回调函数,不传依赖数组:

  • useEffect(callBack)

挂载阶段执行一次后不再执行:传入回调函数,且此函数的返回值不是函数,同时传入空数组。

  • useEffect(()=>{/*业务逻辑*/},[])

仅在挂载和卸载阶段执行的:传入回调函数,且此函数的返回值是一个函数

1
2
3
4
5
6
useEffect(()=>{
//业务逻辑
//返回一个函数记为B
return ()=>{
}
},[])

每次渲染都触发,且卸载阶段也会被触发的:传入回调函数,且这个函数的返回值是一个函数,同时不传第二个参数

1
2
3
4
5
6
7
8
useEffect(()=>{
//A的业务逻辑

//返回一个函数记为B
return ()=>{

}
})

上面这段代码在每次渲染都触发A逻辑,并在卸载阶段触发B逻辑

按我的理解,在前面一半写个函数会在渲染阶段一直触发,单纯写一段逻辑就会在挂载阶段触发,在return那写个函数就会在卸载的时候触发

image-20220313160121838

不要在循环,条件或嵌套函数中调用Hook

首次渲染过程

image-20220314135945234

hook相关的所有信息收敛在一个hook对象粒,而hook对象之间以单向链表的形式相互串联,

更新过程

image-20220314140737526

因此,hooks的渲染是通过“依次遍历”(也就是说,它只会管这回需要前进几个next,而不会对对应位置的真实性进行判断)来定位每个hooks的内容的,如果前后两次读到的链表顺序出现差异,那么渲染的结果自然是不可控的。

React学习20220312 生命周期

React学习20220312

CreateElement函数拆解

image-20220312150556893 image-20220312150653145 image-20220312150814067 image-20220312150922420

生命周期

image-20220312151139289

React16

image-20220312152345970

挂载过程

image-20220312152839851

getDerivedFromProps的功能:使用props来派生/更新state

  • 是针对某个属性的定向更新

更新过程

image-20220312153339553

getSnapshotBeforeUpdate:

image-20220312154540844 image-20220312154512940

React16引入的Fiber架构

之前组件更新:树状递归更新,同步更新,占用主线程,易卡死

Fiber会把大任务转为小任务,使得渲染线程可打断

重要特征

可以被打断的异步渲染模式

react将渲染过程分为render和commit两个过程

render可被打断

commit同步执行

因为commit会改视图,因此不可打断

数据的流动

基于props的单向数据流:

当前组件的state以props的形式流动时,只能流向组件树中比自己层级更低的组件

父子组件通信

父组件把this.props传入子组件

子父组件通信

父组件向子组件传递一个绑定了自身上下文的函数

子组件在调用该函数时,

就可以把想要交给父组件的数据以函数入参的形式传入

image-20220312163654393 image-20220312163734257 image-20220312163752751