swiper 虚拟slide on事件_由浅及深实现虚拟DOM和DOM-Diff

ef110c652f6a5f478750723a9af984fd.png

由浅及深实现虚拟DOM和DOM-Diff

一、前言

随着前端框架比如Vue和React的不断发展,虚拟DOM和DOM-Diff也随着这些框架被越来越多的人重视。在学习和面试的过程中,越来越成为我们无法回避的知识点。面试时经常会被问到:了解虚拟DOM吗?知道Vue和React的虚拟DOM是什么样的吗?知道他们的DOM-Diff是如何实现的吗?如果你没有认真去看过他们的源码,可能会一问三不知。但是去看源码对一些新手又会觉得有点困难,而且难以理解。因此,我的一贯的思想是,如果你想要熟练掌握一个东西,最好的方式就是去实现一个简单的这种东西。
在我之前的文章中,我想要学习webpack,那么我就手动实现一个简易的模块打包器。我想了解loader,那么我就手动去制作了一个loader由浅及深实现一个loader。通过这些简单地实现,可以帮助我们很好地去理解这个东西,这样的话你再去看源码就会觉得轻松很多,毕竟底层原理是相同的,可能更多的是实现细节的优化。
因此,本文的学习重点是虚拟DOM以及DOM-Diff,那么我们就会实现一个简单的虚拟DOM和DOM-Diff。注意,本文的实现过程跟Vue或者React的实现不一定相同,是参考了网上的一些文章,以及个人的理解,是为了尽可能地帮助大家理解,所以实现过程可能不是最优。

二、对虚拟DOM的一些基础知识的理解

2.1 什么是虚拟DOM?

对于虚拟DOM可能很多没有了解过的人会觉得很高大上,不知道是个什么东西,事实上虚拟DOM是相对于真实DOM来进行阐述的。我们都知道一个真实的DOM就是我们常写的html,如下如所示:

<div id="test">
        <p class = "item">节点1</p>
        <span class = "item">节点2</span>
    </div>

而虚拟DOM就是一个js对象,用来描述真实的DOM,它可能是长这样:

const vNode = {
    
    tag:"div",//标签名或者组件名
    data:{
    
        id:"test",
    },
    children:[
        {
    tag:"p",data:{
    className:"item"},children:"节点1"},
        {
    tag:"span",data:{
    className:"item"},children:"节点2"}
    ]
}

我们可以看到上面通过一个vNode对象来描述我们之前的真实DOM,这个对象就是虚拟DOM,对象里面的字段比如tag用来描述标签名称,data用来描述标签上的属性,children用来描述标签上的子元素。在Vue和React中不同的虚拟DOM,可能有不同的字段来描述,但是他们的功能都是为了描述真实DOM。也就是说虚拟DOM是一个对象,它是用来描述真实DOM。大家牢记这句话即可。

2.2为什么需要虚拟DOM?

很多人可能会奇怪,既然已经有了真实DOM,那么为什么还需要虚拟DOM了?万事万物,凡存在即合理。虚拟DOM的存在肯定是因为它带来了好处,相对应的也就是说真实DOM存在一些问题。这里我们参考网上一些常见的原因。

2.2.1 操作真实DOM性能开销大

78318c4f1c15843f0ce2804c1f10640c.png

如上图所示,我们可以看到一个简单的div元素,它身上的属性都非常庞大。当需要操作的DOM非常多,且操作非常频繁时,触发浏览器的渲染。由于浏览器的渲染涉及到:创建DOM树,创建CSSOM树,创建render树,布局和绘制这些流程。当频繁操作DOM时,会频繁地进行渲染,这样的话可能会带来性能和用户体验上的影响。因此,我们希望能够尽可能地去减少DOM的操作。而虚拟DOM的目的就是为了减少真实DOM的频繁操作。那么虚拟DOM就一定能够减少真实DOM的操作吗?或者换句话说虚拟DOM就一定会比真实DOM快吗?这是不一定的。比如说同样是创建10个元素,如果是真实DOM,直接创建10个元素,如果是虚拟DOM,最终还是需要渲染10个元素,反而还增加了虚拟DOM的创建时间,以及虚拟DOM-Diff时间,可能最终耗时反而比真实DOM更长。那么什么情况下使用虚拟DOM能够比使用真实DOM可能更快了。在以下两种情况下,虚拟DOM能够减少真实DOM的操作。

2.2.2 虚拟DOM减少DOM操作的两种情况

  1. 虚拟DOM合并多次操作 当我们需要进行多次DOM操作时,比如第一次修改样式宽度,第二次修改样式高度,第三次修改位置。如果是真实的DOM,那么每修改一次就会触发一次渲染,影响性能。但是虚拟DOM可以合并这几次操作,将所有的样式修改合并到一起修改,这样的话就相当于只修改了一次DOM,触发一次渲染。
  2. 虚拟DOM可以减少操作范围 当我们需要更新100个节点时,虚拟DOM可以通过DOM Diff发现只有10个需要更新,这样的话就只需要操作10个即可。从而减少操作DOM的范围。

2.3 虚拟DOM组成

在2.1节中,我们知道了虚拟DOM是一个对象,对象的一个一个属性用来描述虚拟DOM,而且不同的虚拟DOM其属性也不同,但是最终他们渲染出来的真实DOM确是一致的,这说明虚拟DOM有其固定的组成。我们看下React和Vue中虚拟DOM的组成,然后分析出他的一些必不可缺的组成部分。

React

const vNode = {
    
    type:"div",         // 标签名或者组件名
    key:null,
    props:{
                 // 属性
        children:[      // 子元素或者子组件
            {
    type:"span",...},
            {
    type:"div",...},
        ],
        className:"wrapper",
        onClick:() => {}
    },
    ref:null,
    ...
}

Vue

const vNode = {
    
    tag:"div",          //标签名或者组件名
    data:{
                  // 属性
        class:"wrapper",
        on:{
    
            click:() => {}
        }
    },
    children:[          // 子元素或者子组件
        {
    tag:"span",...},
        {
    tag:"div",...},
    ],
    ...
}

通过上面的对比,无论是React还是Vue中虚拟DOM的组成都肯定包含三个部分:

  1. type/tag:元素类型
  2. props/data:元素属性
  3. children:子元素集合

事实上,这也是描述一个真实DOM所不可或缺的三部分。type用来描述DOM的类型,是一个普通元素还是组件,

props用来描述这个元素身上的属性比如常见的style,class以及事件等,而children用来描述这个元素内部包裹的子元素。只有这三个都存在,才能够完整地描述一个元素。因此,我们知道了,一个虚拟DOM它至少应该是下面这种形式:

const vNode = {
    
    tag:"div",
    data:{
    
        id:"test",
        class:"item"
    },
    children:[
        {
    tag:"span",data:{},children:"span1"}
    ]
}

知道了虚拟DOM的具体组成,那么接下来我们就需要知道如何去创建虚拟DOM了。

三、虚拟DOM的创建和渲染

3.1 实现createElement函数创建虚拟DOM

通过上面的分析,我们已经知道了什么是虚拟DOM,为什么使用虚拟DOM以及虚拟DOM的组成。接下来我们就需要实现一个虚拟DOM。也就是说我们需要知道如何去生成虚拟DOM。事实上,创建虚拟DOM实际就是去创建一个如下的js对象。

{
    
    tag:"div",
    data:{
    
        id:"test",
        class:"item"
    },
    children:[
        {
    tag:"span",data:{},children:"span1"}
    ]
}

想要创建虚拟DOM,那么需要借助函数来实现,我们需要一个函数createElement能够返回上面的数据,如下如所示:

function createElement(tag,data,children){
    
    return {
    
        tag,
        data,
        children
    }
}

这是一个最简单的生成vNode的函数,我们使用这个函数来生成下面真实DOM的vNode。

<div id="test">
        <p class = "item"></p>
    </div>

通过createElement生成对应的vNode

var vNode = createElement("div",{
    id:"test"},[
        createElement("p",{
    class:"item"},"p1")
    ])
    console.log(JSON.stringify(str,null,2))

查看得到的结果是:

{
    
  "tag": "div",
  "data": {
    
    "id": "test"
  },
  "children": [
    {
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值