react中的虚拟dom和diff算法详解

虚拟DOM的作用

首先我们要知道虚拟dom的出现是为了解决什么问题的,他解决我们平时频繁的直接操作DOM效率低下的问题。那么为什么我们直接操作DOM效率会低下呢?

比如我们创建一个div,我们可以在控制台查看一下这个div上自带或者继承了很多属性,尤其是我们使用js操作DOM的时候,我们的DOM本身就很复杂,js的操作也会占用很多时间,但是我们控制不了DOM元素本身,因此虚拟DOM解决的是js操作DOM这一层面其实解决的是减少了操作dom的次数

简单实现虚拟DOM

虚拟DOM,见名知意,就是假的DOM,我们真实的DOM挂载在页面上的,而我们的虚拟DOM则是在内存中的。这个就需要我们把真实的DOM抽象成一个对象放在内存中。这个对象就可以是如下类型:

var element = {
      tagName: 'div',
      props: {
        class: 'box'
      },
      children: {
        {
          tagName: 'p',
          props: {
            class: 'p1'
          },
          children: ['我是p1']
        },
         {
          tagName: 'p',
          props: {
            class: 'p2'
          },
          children: ['我是p2']
        },
        {
          tagName: 'p',
          props: {
            class: 'p3'
          },
          children: ['我是p3']
        },
      }
    }

我们想要构造出这样的对象可以自己封装一个构造函数如下:

function Element(tagName, props, children) {
    this.tagName = tagName
    this.props = props
    this.children = children
}

有了这个对象,我们需要把这个虚拟DOM渲染到真实DOM上,可以写出如下方法:

Element.prototype.render = function () {
    const { tagName, props, children } = this
    var el = document.createElement(tagName)
    for (key in props) {
        el.setAttribute(key, props[key])
    }
    children.forEach((item) => {
        const childEl = (item instanceof Element) ?
              item.render() :
        document.createTextNode(item)
        el.appendChild(childEl)
    })
    return el
}

最后我们可以new出这个对象调用render()方法然后appendChild到body中就好了:

let virtualDom = new Element('div', { class: 'box' }, [
    new Element('p', { class: 'p1' }, ['我是p1']),
    new Element('p', { class: 'p2' }, ['我是p2']),
    new Element('p', { class: 'p3' }, ['我是p3']),
])

let a = virtualDom.render()
document.body.appendChild(a)

diff算法

首先我们先了解一下diff算法的作用

如果我们的虚拟dom发生了变化,我们的内存中又会产生新的虚拟DOM,如果我们直接用这个新的虚拟DOM结构的话,又会导致很多重复的渲染,因此 这个时候diff算法的作用就体现了出来,diff通过比较新旧两个虚拟DOM树,找出差异,并且记录下来,然后把记录的差异应用到真实的DOM树上。

原理:

diff算法通过对新旧两颗树进行深度优先遍历,每一个节点都加一个唯一的标识。

这个过程分为2步

  • 找出两个树的差异,并记录在一个伪数组里。
  • 把这些不同应用到真实的DOM树上

对于dom的操作基本可化为4种类型

  • 对节点的删除,移动,添加子节点
  • 更换节点标签
  • 对于文本节点,修改节点文本
  • 修改节点props

下面会用伪代码的形式大致过一下这个流程

// diff 函数,对比两棵树
function diff(oldTree, newTree) {
    var patchs = {};  // 伪数组,记录差异
    //  对4种节点做错判断
    dfWork(oldTree, newTree, patchs, index)

    return patchs

}
function dfWork(oldTree, newTree, patchs, index) {
    let currentPatch = []

    if (1) { // 对节点的删除
        currentPatch.push()
    } else if (3) { // 对节点的文本的更换
        currentPatch.push()

    } else { // 修改节点的props 对children的检查
        // 对props作diff算法,把变化记录到patchs中。
        currentPatch.push({ type: patch.PROPS, props: propsPatches })
        // 然后需要对子节点作diff算法
        diffChildren(oldNode.children, newNode.children, index, patches, currentPatch)
    }
}
function diffChildren(oldChildren, newChildren, index, patches, currentPatch) {

    // 对子节点作diff算法,遍历子节点,递归调用dfWork,做差异得到patchs

}
// 把变化应用在真实的DOM树上
function patch(node, patchs) {
    // node为老的DOM树,patchs变化。
    // 我们会遍历这个patchs,并且把node和patch对应上,
}
function applyPatch(node, patchs) {
    // 应为每个节点可能有多个变化,所以也需要遍历
    switch (patchs.type) {
        case REPLACE: // 节点替换
            // node.render()
            break;
        case REORDER:  // 节点的移动删除新增子节点。

            break;
        case PROPS:
            // setProps
            break;
        case TEXT: // 对节点文本的修改
            // node.nodeValue
            break;

        default:
            break;
    }
}

参考文档:深度剖析:如何实现一个 Virtual DOM 算法 作者:livoras,内置源码。

到此这篇关于react中的虚拟dom和diff算法的文章就介绍到这了,更多相关react虚拟dom和diff算法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • React diff算法的实现示例

    前言 在上一篇文章,我们已经实现了React的组件功能,从功能的角度来说已经实现了React的核心功能了. 但是我们的实现方式有很大的问题:每次更新都重新渲染整个应用或者整个组件,DOM操作十分昂贵,这样性能损耗非常大. 为了减少DOM更新,我们需要找渲染前后真正变化的部分,只更新这一部分DOM.而对比变化,找出需要更新部分的算法我们称之为diff算法. 对比策略 在前面两篇文章后,我们实现了一个render方法,它能将虚拟DOM渲染成真正的DOM,我们现在就需要改进它,让它不要再傻乎乎地重新渲

  • react diff算法源码解析

    React中Diff算法又称为调和算法,对应函数名为reconcileChildren,它的主要作用是标记更新过程中那些元素发生了变化,这些变化包括新增.移动.删除.过程发生在beginWork阶段,只有非初次渲染才会Diff. 以前看过一些文章将Diff算法表述为两颗Fiber树的比较,这是不正确的,实际的Diff过程是一组现有的Fiber节点和新的由JSX生成的ReactElement的比较,然后生成新的Fiber节点的过程,这个过程中也会尝试复用现有Fiber节点. 节点Diff又分为两种

  • 浅谈从React渲染流程分析Diff算法

    React中最神奇的部分莫过于虚拟DOM,以及其高效的Diff算法.这让我们可以无需担心性能问题而"毫无顾忌"的随时"刷新"整个页面,由虚拟DOM来确保只对界面上真正变化的部分进行实际的DOM操作.React在这一部分已经做到足够透明,在实际开发中我们基本无需关心虚拟DOM是如何运作的.然而,理解其运行机制不仅有助于更好的理解React组件的生命周期,而且对于进一步优化React程序也会有很大帮助. 1.什么是虚拟DOM 在React中,render执行的结果得到的

  • 详解react应用中的DOM DIFF算法

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

  • React Diff原理深入分析

    在了解Diff前,先看下React的虚拟DOM的结构 这是html结构 <div id="father"> <p class="child">I am child p</p> <div class="child">I am child div</div> </div> 这是React渲染html时的js代码   自己可以在babel上试试 React.createElemen

  • react中的虚拟dom和diff算法详解

    虚拟DOM的作用 首先我们要知道虚拟dom的出现是为了解决什么问题的,他解决我们平时频繁的直接操作DOM效率低下的问题.那么为什么我们直接操作DOM效率会低下呢? 比如我们创建一个div,我们可以在控制台查看一下这个div上自带或者继承了很多属性,尤其是我们使用js操作DOM的时候,我们的DOM本身就很复杂,js的操作也会占用很多时间,但是我们控制不了DOM元素本身,因此虚拟DOM解决的是js操作DOM这一层面,其实解决的是减少了操作dom的次数 简单实现虚拟DOM 虚拟DOM,见名知意,就是假

  • vue中虚拟DOM与Diff算法知识精讲

    目录 前言 知识点: 虚拟DOM(Virtual DOM): 虚拟dom库 diff算法 snabbdom的核心 init函数 h函数 patch函数(核心) 题外话:diff算法简介 传统diff算法 snabbdom的diff算法优化 updateChildren(核中核:判断子节点的差异) 新结束节点和旧结束节点(情况2) 旧结束节点/新开始节点(情况4) 前言 面试官:"你了解虚拟DOM(Virtual DOM)跟Diff算法吗,请描述一下它们"; 我:"额,...鹅

  • 一篇文章带你搞懂Vue虚拟Dom与diff算法

    前言 使用过Vue和React的小伙伴肯定对虚拟Dom和diff算法很熟悉,它扮演着很重要的角色.由于小编接触Vue比较多,React只是浅学,所以本篇主要针对Vue来展开介绍,带你一步一步搞懂它. 虚拟DOM 什么是虚拟DOM? 虚拟DOM(Virtual   Dom),也就是我们常说的虚拟节点,是用JS对象来模拟真实DOM中的节点,该对象包含了真实DOM的结构及其属性,用于对比虚拟DOM和真实DOM的差异,从而进行局部渲染来达到优化性能的目的. 真实的元素节点: <div id="wr

  • 深入聊一聊虚拟DOM与diff算法

    目录 虚拟DOM与diff算法 snabbdom环境搭建 虚拟DOM和h函数 diff算法 patch函数 patchVnode函数 updateChildren函数 v-for中key作用与原理 总结 虚拟DOM与diff算法 在vue.react等技术出现之前,每次修改DOM都需要通过遍历查询DOM树的方式,找到需要更新的DOM,然后修改样式或结构,资源损耗十分严重.而对于虚拟DOM来说,每次DOM的更改就变成了JS对象的属性的更改,能方便的查找JS对象的属性变化,要比查询DOM树的性能开销

  • Vue3 diff算法之双端diff算法详解

    目录 双端Diff算法 双端比较的原理 简单Diff的不足 双端Diff介绍 Diff流程 第一次diff 第二次diff 第三次diff 第四次diff 双端Diff的优势 非理想情况的处理方式 添加新元素 移除不存在得节点 双端Diff完整代码 双端Diff算法 双端Diff在可以解决更多简单Diff算法处理不了的场景,且比简单Diff算法性能更好.本篇是以简单Diff算法的案例来展开的,不过没有了解简单Diff算法直接看双端Diff算法也是可以看明白的. 双端比较的原理 引用简单Diff算

  • React中immutable的UI组件渲染性能详解

    目录 引言 UI组件渲染性能 方案一:shallow compare 方案二:直接对前后的对象进行deepCompare 总结: 引言 react 一直遵循UI = fn(state) 的原则,有时候我们的state却和UI不同步 有时候组件本身在业务上不需要渲染,却又会再一次re-render.之前在项目中遇到的一些问题,这里做一个简单的分析,大家可以一起交流一下 UI组件渲染性能 react每次触发页面的更新可大致分成两步: render(): 主要是计算v-dom的diff commit阶

  • Vue的虚拟DOM和diff算法你了解吗

    目录 什么是虚拟DOM? 为什么需要虚拟DOM? 总结 在vue 中 数据改变 -> 虚拟DOM(计算变更)-> 操作DOM -> 视图更新 虚拟DOM: js执行速度比较快 什么是虚拟DOM? 用JS模拟DOM结构 为什么需要虚拟DOM? vue中 数据驱动视图,需要用高效方法来控制DOM操作的次数 diff算法: 虚拟DOM的核心 patch函数 两个使用场景: 首次渲染时,判断第一个参数是否是一个真实dom元素,是的话就创建空vnode,并且关联一个DOM元素,然后比较patch函

  • React中使用collections时key的重要性详解

    前言 大家应该都知道,在 React 中 render collections 的 items 时, Keys 扮演着重要的角色, 它直接决定接下来的 rendered 和 re-rendered,下面话不多说,来一起看看详细的介绍: React 不会 render 重复的 keys 为了彻底明白这个, 我们来声明一个这样的数组 const nums = [1, 2, 3, 5, 2]; // 它有两个元素的值是相等的 现在, 我们在 react 中来 render <ul> {nums.ma

  • 关于react中组件通信的几种方式详解

    前言 刚入门React可能会因为React的单向数据流的特性而遇到组件间沟通的麻烦,下面这篇文章就来给大家详细介绍下,在开始之前先来看一张图: react组件通信 需要组件之进行通信的几种情况 父组件向子组件通信 子组件向父组件通信 跨级组件通信 没有嵌套关系组件之间的通信 1. 父组件向子组件通信 React数据流动是单向的,父组件向子组件通信也是最常见的;父组件通过props向子组件传递需要的信息 Child.jsx import React from 'react'; import Pro

  • java 中冒泡、二分、快速算法详解

    1.冒泡算法的原理: 冒泡排序算法的一般性策略:搜索整个值列,比较相邻元素,如果两者的相对次序不对,则交换它们,其结果是最大值"想水泡一样"移动到值列的最后一个位置上,这也是它在最终完成排序的值列中合适的位置.然后再次搜索值列,将第二大的值移动至倒数第二个位置上,重复该过程,直至将所有元素移动到正确的位置上. 下面是两个Java冒泡算法程序 2.冒泡代码如下: public class BubbleSort { public static void bubbleSort(int[] a

随机推荐