服务器之家:专注于服务器技术及软件下载分享
分类导航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服务器之家 - 编程语言 - JavaScript - React - 详解react应用中的DOM DIFF算法

详解react应用中的DOM DIFF算法

2022-02-25 16:22time_w React

这篇文章主要介绍了react应用中的DOM DIFF算法,帮助大家更好的理解和学习使用react,感兴趣的朋友可以了解下

前言

对我们搞前端的来说,目前最流行的两大前端框架毫无疑问当属React和Vue,对于这两大框架,想必大家也是再熟悉不过了。然而,这两大框架无一例外的全部放弃使用传统的DOM技术,却采用了以JS为基础的Virtual DOM技术,也可称作虚拟DOM。所以,到底什么是Virtual DOM?两大热门框架全部使用Virtual DOM的原因又是什么?接下来让我这个搞前端的人来好好地为您讲解一下DOM DIFF算法的牛逼之处。

什么是Virtual DOM?

如字面意思所说,Virtual DOM即 虚拟DOM,它的特点就是利用Javascript来模拟DOM结构,并且将DOM的变化对比放在JS层中进行比较。具体操作如下:


  1. // 普通的HTML DOM结构
  2. <ul class="list">
  3. <li class="item">wjy</li>
  4. <li class="item">易烊千玺</li>
  5. </ul>
  6.  
  7. // 映射出的虚拟DOM
  8. {
  9. tag:'ul',
  10. props:{
  11. class:'list'
  12. },
  13. children: [
  14. {
  15. tag:'li',
  16. props:{
  17. class:'item'
  18. },
  19. children: ['易烊千玺']
  20. }
  21. ]
  22. }

有朋友或许会有点迷惑,普通的HTML DOM结构不好吗?通俗易懂,代码也更加简洁,为什么还需要采用嵌套递归的虚拟DOM形式呢?其实这和普通DOM在重新渲染的过程中非常消耗性能有关,DOM操作看似简便,但其实效率相当低,这是因为如果是在需要频繁修改的真实DOM中,看起来更加复杂的运用JS结构的Virtual DOM效率会更高.

使用Virtual DOM的原因

DOM 渲染页面的操作流程

  • 当浏览器通过域名从服务器拿到对应的HTML文件后,浏览器首先会进行构建DOM树和CSSOM树,关于树的概念,学过数据结构与算法的同学或许对树的节点概念印象深刻。
  • 在HTML DOM中,所有的事物都是节点,而DOM是被视为节点树的HTML,并且,各个节点之间有着相应的层级关系。DOM节点树和HTML中的标签一一对应,构成了DOM树
  • HTML文件

详解react应用中的DOM DIFF算法

  • HTML DOM树

详解react应用中的DOM DIFF算法

同样的。在CSS文档中,所有的元素也皆是节点,与HTML中的标签一一对应,构成了CSSOM树 如下图所示

详解react应用中的DOM DIFF算法

警告! 如果在构建DOM树的过程中,有遇到JS相关的内容时,DOM树的构建会立即停止,这是由于,JS可以对DOM节点进行操作,浏览器为了防止JS会对以完成的DOM造成影响,会阻止DOM树的构建,以节约资源。

在DOM树和CSSOM树不断构建的过程中,渲染树也在逐渐形成,浏览器会根据所构建的渲染树进行网页布局和绘制流程,不断地进行网页的搭建。具体操作流程图如下:

详解react应用中的DOM DIFF算法

一般而言,对于页面渲染的常规操作,我们通常是操作DOM,修改并重置innerHTML完成页面的渲染,每进行一次DOM的更新操作,都会重新进行一次渲染流程,这个过程包含着页面的重绘和重排。

Virtual DOM的优势

详解react应用中的DOM DIFF算法

但是如果对于大型页面项目,或者具有多标签,多属性的网页而言,常规的DOM操作实在是太耗时了,每次的简单修改都需要牵动大量的DOM节点的重绘与重排,极大地降低了页面渲染效率.于是,当前端开发人员面对DOM瓶颈一筹莫展的时候,Virtual DOM显示出了作为轻量级的JavaScript对象的极大优越性,顺利得到了了前端开发者的青睐。

在页面进行重新渲染的时候,Virtual DOM进行dom diff计算对比两次并发现其中的差异,只需要修改DOM树中不同的部分即可.也可以理解为Virtual DOM做了一个中间件,先利用JS修改Virtual DOM,在对比出差异后,将所有的更改加入页面的真实DOM.所以说,Virtual DOM的最大优势就在于完全不用像原生DOM,在对比之后还要进行DOM的重建与创造,因为这对于大型项目的运行来说非常消耗性能,开销极大.由此可见,不论在什么体量的网页中,放弃传统DOM采用Virtual DOM无疑是非常高效且绝佳的选择.

如何将DOM用virtual DOM 来表示

首先,在vscode中新建一个dom diff 项目,项目初始化,装好相应组件

由于是DOM树,所以在将HTML转换成DOM树时,要运用递归的形式,首先创建结点,其次设置属性,然后设置子节点


  1. <ul class="list">
  2. <li class="item">wjy</li>
  3. <li class="item">易烊千玺</li>
  4. </ul>
  5.  
  6. // DOM 树的表达转换形式
  7. let virtualDOM = createElement('ul', {
  8. class:'list',
  9. }, [
  10. createElement('li',{
  11. class:'item'
  12. },['wjy']),
  13. createElement('li',{
  14. class:'item'
  15. },['易烊千玺']),
  16. ])

然后,在新建一个element.js文件进行向外输出,完成页面渲染


  1. // 通过构造函数Element构造虚拟DOM节点
  2. class Element {
  3. constructor(type,props,children){
  4. this.type = type;
  5. this.props = props;
  6. this.children =children;
  7. }
  8. }
  9. //
  10. const createElement = (type,props,children) => {
  11. return new Element(type,props,children);
  12. }
  13.  
  14. // 进行页面渲染 将Virtual Dom转化为真实DOM
  15. const render = (domObj) => {
  16. let el = document.createElement(domObj.type);
  17. for(let key in domObj.props){
  18. setAttr(el,key,domObj.props[key]);
  19. }
  20. domObj.children.forEach(child => {
  21. child = (child instanceof Element)
  22. ? render(child)
  23. : document.createTextNode(child);
  24. el.appendChild(child);
  25.  
  26. })
  27. return el;
  28. }
  29.  
  30. function setAttr(node,key,value){
  31. switch(key){
  32. case 'value':
  33. if(node.tagName.toLowerCase() === 'input' ||
  34. node.tagName.toLowerCase() ==='textarea'
  35. ){
  36. node.value = value;
  37. }else{
  38. node.setAttribute(key,value)
  39. }
  40. break;
  41. case 'style':
  42. // node.setAttribute('style',value)
  43. node.style.cssText = value;
  44. break;
  45. default:
  46. node.setAttribute(key,value)
  47. break;
  48. }
  49. }
  50.  
  51. // 将真实DOM 挂载到制定根节点
  52. const renderDOM = (el,target) => {
  53. target.appendChild(el);
  54. }
  55.  
  56. // 向外输出
  57. export {
  58. createElement,
  59. render,
  60. renderDOM
  61. }

在控制台上得到真实DOM

详解react应用中的DOM DIFF算法

页面渲染成功!

详解react应用中的DOM DIFF算法

DOM DIFF算法

在用户进行操作更改交互页面操作后,虚拟DOM树上的节点会发生变化,然而此时真实节点却没有改变,为了使得更改与真实页面同步,我们会使用DOM DIFF算法找出这两颗树的差异,然后产生差异补丁对象,再将差异补丁对象应用到真实的DOM节点上去,于是完成了页面的渲染和更新。

传统的Diff算法时间复杂度达到了O(n^3),若要满足每次都可以整体刷新页面的目的,这种指数型的增长的性能开销是无法满足性能要求的,于是,Facebook的工程师对此进行了优化,通过制定diff策略将Diff算法的复杂度降低到了O(n)

Diff 策略

  • DOM节点跨层级的操作特别少,所以可以忽略不计
  • 拥有相同类的两个组件将会产生相似的树形结构,拥有不同类的两个组件将会产生不同的树形结构
  • 同一层级的一组子节点,他们可以通过uuid进行区分

Diff 粒度

由于DIFF的粒度不同,DIFF算法按照下面的顺序依次执行

  • Tree DIFF
  • Component DIFF
  • Element DIFF

打补丁

我们根据diff策略以及react diff中的比对算法将两个虚拟DOM通过深度优先遍历进行比较,如果有差异,就把所遍历到节点的索引值所对应的操作存储起来,也称为补丁对象(patches)

然后对真实的DOM再次经过深度优先遍历,补丁对象中的索引就会和DOM相对应,我们就完成了DOM的更新操作。

结语 在互联网环境下,随时刷新交互页面是我们上网的常规操作,然而这一简单的操作却是多次算法优化的结果。DOM DIFF的底层原理挺复杂,如果有感兴趣的朋友,可以自行搜索相关文献,因为本文只是浅析,所以太多方面就不赘述了,如果本文有知识错漏的地方,也欢迎指正!虚心接收一切合理批评!

如果这篇文章有帮助到你对dom diff算法的理解,也希望您能为我点一个赞,答主是刚入门的前端小白,每一个赞都是我前进的动力,我会持续更新掘金的博客的

以上就是详解react应用中的DOM DIFF算法的详细内容,更多关于react应用的DOM DIFF算法的资料请关注服务器之家其它相关文章!

原文链接:https://juejin.cn/post/6948696440935809031

延伸 · 阅读

精彩推荐
  • React不用一行代码,搞懂React调度器原理

    不用一行代码,搞懂React调度器原理

    本文会讲解React调度器Scheduler的实现原理。知道你不喜欢看大段的代码,所以本文没有一行代码。文末有Scheduler的源码地址,感兴趣的话可以去看看。...

    魔术师卡颂10562021-12-26
  • ReactVite搭建React项目的方法步骤

    Vite搭建React项目的方法步骤

    这篇文章主要介绍了Vite搭建React项目的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面...

    Cookieboty5132022-02-24
  • ReactReactRouter的实现方法

    ReactRouter的实现方法

    这篇文章主要介绍了ReactRouter的实现,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...

    WindrunnerMax6172022-01-06
  • Reactreact-native 实现购物车滑动删除效果的示例代码

    react-native 实现购物车滑动删除效果的示例代码

    这篇文章主要介绍了react-native 实现购物车滑动删除效果的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,...

    程序猿tx3882021-12-31
  • ReactReact使用emotion写css代码

    React使用emotion写css代码

    这篇文章主要介绍了React如何使用emotion写css代码,帮助大家更好的理解和学习使用React,感兴趣的朋友可以了解下...

    joychenke8162022-02-25
  • ReactReact中setState的使用与同步异步的使用

    React中setState的使用与同步异步的使用

    这篇文章主要介绍了React中setState的使用与同步异步的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋...

    一颗冰淇淋5232022-02-17
  • React使用react从零封装一个可实时预览的Json编辑器

    使用react从零封装一个可实时预览的Json编辑器

    文章将介绍如何使用react,开发一个自定义json编辑器组件.我们这里使用了jsoneditor这个第三方库。...

    趣谈前端7502022-01-12
  • Reactreact项目从新建到部署的实现示例

    react项目从新建到部署的实现示例

    这篇文章主要介绍了react项目从新建到部署的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们...

    juejin9402022-01-21