VDOM(虚拟dom)

react和vue都是基于vdom的前端框架。

web界面由DOM树来构建,当其中一部分发生变化时,其实就是对应的某个节点发生了变化。

若一次操作中有十次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这十次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,再进行后续的操作。

用js对象模拟DOM节点的好处是:

  • 页面的更新可以先全部反映在js对象(虚拟DOM)上,精准的对比关心的属性,避免大量无谓的计算。等更新完成后,再将最终的js对象映射成真是的DOM,交由浏览器去绘制。
  • 为应用带来的跨平台的能力,不再仅仅局限于浏览器端。比如:React-Native、canvas等。
//虚拟dom对象
{
    type: 'div',
    props: {
        id: 'aaa',
        className: ['bbb', 'ccc'],
        onClick: function() {}
    },
    children: []
}

react中,jsx由babel转义再经过render后生成我们想要的VDOM(虚拟DOM)。

Fiber架构

react15的时候,和 vue 的渲染流程还是很像的,都是递归渲染 vdom,增删改 dom 就行。

但是因为状态管理的差异导致了架构的差异。react的setState会渲染整个vdom,而一个应用的所有 vdom 可能是很庞大的,计算量就可能很大。

浏览器里 js 计算时间太长是会阻塞渲染的,会占用每一帧的动画、重绘重排的时间,这样动画就会卡顿。

那能不能把计算量拆分一下,每一帧计算一部分,不要阻塞动画的渲染呢?

顺着这个思路,react 就改造为了 fiber 架构,目标是打断计算,分多次进行。

渲染的时候不要直接更新到 dom 了,只找到变化的部分,打个增删改的标记,创建好 dom,等全部计算完了一次性更新到 dom 就好了。

初始化渲染

根据 React Element 生成对应的 Fiber 树

1.根据 React Element 生成对应的 Fiber 树

首次执行ReactDOM.render 会创建fiberRoot 和 rootFiber。

  1. fiberRoot:整个应用的根节点(只能有一个)
  2. rootFiber:组件树的根节点(可以有多个)

2.根据组件返回的JSX在内存中依次创建Fiber节点,并连接在一起构建形成Fiber树,被称为workInProgress Fiber树。最后以 workInProgress 作为最新的渲染树,fiberRoot 的 current 指针指向 workInProgress 使其变为 current Fiber 树。到此完成初始化流程。

  1. workInProgress:正在内存中构建的 Fiber 树称为 workInProgress Fiber树。在一次更新中,所有的更新都是发生在 workInProgress 树上。在一次更新之后,workInProgress 树上的状态是最新的状态,那么它将变成 current 树用于渲染视图。
  2. current:正在视图层渲染的树叫做 current 树。

更新时

render阶段,创建 dom,打上增删改的 tag,等全部计算完之后,commit 阶段一次性更新到 dom。

render阶段

beginWork和completeWork阶段会循环最新的jsx生成的虚拟dom,通过对比虚拟dom和current Fiber树生成workinProgress Fiber树。

beginWork

创建本次循环主体的子Fiber节点

  • mount(首屏渲染)时创建子Fiber节点,并返回改新建节点;
  • update时若不满足复用条件,则与mount时一样创建新的子fiber节点,并diff出相应的effTag挂在FIber节点上,并返回该新建节点;
  • updata时若满足复用条件,且判断仍需处理其子节点的后代,则返回复用后的子Fiber节点
  • updata时若满足复用条件,且判断不需继续处理其子节点的后代,则直接返回null值;

completeWork

构建或更新DOM节点

  • 构建过程中,会自下而上将子节点插入到当前节点
  • 更新过程中,会计算DOM 节点的属性,一旦属性需要更新,会为DOM节点对应的workINProgress节点标记Update的effectTag

自下而上收集effectList,最终收集到root上

Diff到底是谁跟谁比?

Diff 是 vdom 和 current Fiber 对比,生成 workInProgressFiber

effectTag与effectList

render阶段不会真正操作dom,只会创建dom然后打个effectTag的增删改标记。commit阶段就根据标记来更新dom就可以了。

但是commit阶段要在遍历一次fiber来查找有effectTag的节点,更新dom吗?

当然没问题,但是显然很低效。完全可以把有 effectTag 的节点收集到一个链表里,然后 commit 阶段直接遍历这个链表就行了。这个链表叫做 effectList。

react 会在 commit 阶段遍历 effectList,根据 effectTag 来增删改 dom。

这个队列叫做 effectList。

commit阶段

render阶段找到变化的部分,创建dom,打上增删改的tag,等全部计算完之后,commit阶段一次性更新到dom。

before mutation阶段(执行DOM操作前)

遍历effectList,依次执行:

1.处理DOM节点渲染/删除后的autoFocus、blur逻辑

2.调用getSnapshotBeforeUpdate生命周期钩子

3.调度useEffect mutation阶段(执行DOM操作)

遍历 effectList 来更新 dom

layout阶段(执行DOM操作后)

因为这个阶段已经可以拿到布局信息了,会同步调用 useLayoutEffect 的回调函数。而且这个阶段可以拿到新的 dom 节点,还会更新下 ref。

到此这篇关于React深入了解原理的文章就介绍到这了,更多相关React原理内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

React深入了解原理的更多相关文章

  1. xcode6.1 – Xcode 6.1中项目模板中缺少类前缀

    项目模板上曾经有一个类前缀字段,这有助于区分项目类和框架类.Xcode6.1项目模板中不再提供此功能.这背后的意图是什么?

  2. ios – 伞框架

    错误.应用程序,通常位于…错误仍然存在你也可以在这里添加(子)框架的路径.

  3. ios – UIView框架大小的问题

    我正在开发一个iPad项目,目前正在使用Landscape视图.我试着这样做:为什么这总是返回960?虽然在景观中,视图本身的高度尺寸应为768对吗?

  4. 安装自定义cocoa框架的最佳方法

    我有一个自定义框架,遵循Apple的框架编程指南>>中的建议.Installingyourframework我在/Library/Frameworks中安装.我通过使用以下脚本添加RunScript构建阶段来完成此操作:在我的项目中,我然后链接/Library/Frameworks/MyFramework并将其导入我的类中,如下所示:这非常有效,除了我总是在调试器控制台中看到以下消息:Loadin

  5. ios – React native链接到另一个应用程序

    如果是错误的,有人知道如何调用正确的吗?

  6. ios – 在设备上构建和运行时,仅将嵌入式框架与其他动态框架链接失败

    TL;博士将您的嵌入式框架与其他框架链接,并且不将其他框架与您的应用程序链接,导致Build&在设备上运行.描述:建立:我的设置非常简单(Swift2.3&XcodeXcode8.0;Build版本8S162m):>使用Carthage(0.17.2)我用xcodebuild8.0和TOOLCHAINS=com.apple.dt.toolchain.Swift_2_3carthagebui

  7. iOS 8嵌入式框架中的头文件

    我正在尝试创建一个用于iOS8的嵌入式框架.在创建一个名为SampleKit(BTW;这里有任何约定,我应该使用前缀吗?)之后,它包含一个令我困惑的头文件:我知道FOUNDATION_EXPORT是extern或extern“C”的宏,但我不确定这两个常量.我应该在哪里为他们设定价值?解决方法项目>构建设置>版本控制>当前项目版本:

  8. 在Monotouch上模拟.NET的框架?

    有没有人使用过他们发现与Monotouch兼容的.NET模拟框架?在尝试使用之前,我很好奇与NMock,NSubstitute,Moq和其他框架的兼容性.Xamarin刚刚加强了它的单元测试支持,但没有提到模拟框架.仅供参考,我希望在VS2010上为非UI位做很多开发,并在UI进入时移动到iOS平台.谢谢您的帮助.解决方法我建议只使用手动模拟:如果我不得不猜测RhinoMocks,Moq等大量使用Reflection.Emit(你怎么能做他们能做的疯狂?),这将无法在MonoTouch上使用AOT编译器运

  9. 在ios上使用来自框架的boost :: filesysystem路径

    我一直在使用Boost作为PeteGoodliffe脚本构建的框架已有一段时间了.效果很好.最近我遇到了一个问题,可以通过将以下代码放入另一个全新的XCode项目中的视图控制器的viewDidLoad中来重现:当路径对象被销毁时会导致EXC_BAD_ACCESS.有没有其他人遇到这个问题?

  10. ios – 在约束依赖于框架的自定义视图中使用自动布局

    我正在编写一个以编程方式初始化的自定义视图.我重写updateConstraints以添加此视图所需的所有约束.:问题是self.bounds返回CGRectZero的等价物.我做了我的研究并根据这个objc.ioarticle,这是预期的,因为在调用layoutSubviews之前框架不会被设置.它也提到了Toforcethesystemtoupdatethelayoutofaviewtreei

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部