美文网首页
Virtual DOM

Virtual DOM

作者: IsaacHHH | 来源:发表于2018-04-24 14:44 被阅读65次

    前端发展至今,从直接的DOM操作到MVC设计,然后到MVP,再到如今的MVVM设计模式,其方向都是朝着更高效、更方便、易维护的角度去发展,极大的方便了我们开发过程。
    但是,我们需要知道的,目前所有的MV*的设计模式,总的来说仅仅改变了一件事情:提高开发效率。其根本仍旧是DOM操作。对于DOM操作所产生的开销,大家可以看这篇文章。简单来说,操作DOM会产生页面重绘和回流,开销就花在这个上面。

    当然,我们要清楚一点是,这里我们优化DOM开销的任何手段都不及浏览器的一次优化升级所产生的效益大,其他优化也是一样。

    为了优化DOM元素运行的效率,前端出现了Virtual DOM(虚拟DOM)。当然,Virtual DOM 也仅仅是减少DOM操作,不可能完全做到不操作DOM。

    为什么要有Virtual DOM

    前面我们简单的说了,为了解决DOM操作所带来的效率问题而设计了Virtual DOM。这里我们通过一段代码来直观的感受下解决了什么问题。

    <ul id='root'>
      <!-- 遍历list -->
      <li h-for='value in list'>{{value}}</li>
    </ul>
    
    // 伪代码
    let viewModel = new VM({
      $el: document.getElementById('root'),
      data: {
        list: [
          {value: 1},
          {value: 2},
          {value: 3}
        ]
      }
    })
    

    这里我模仿Vue的语法来写了一段伪代码,约定了一个以h开头的Directive(指令),虽然实际不可能会运行。

    通过遍历list数组,来显示3个li元素。这个很容易理解。但是当我此时list增加一个{value: 4}的元素:

    list: [
      {value: 1},
      {value: 2},
      {value: 3},
      {value: 4}
    ]
    

    此时,在一般MVVM框架中,页面中会重新渲染DOM,更新View,显示4个li元素。

    但是,在这个过程中, 我们只是希望向ul元素中的尾部添加一个li元素就可以了,没必要再去重新渲染list数据,因为这样会导致前面3个没有改变的数组元素也会重新渲染,这样无疑产生了一次性能浪费。

    所以,此时Virtual DOM就是为了解决这一个问题,不去渲染未改变的数据,而是仅仅通过某种算法,去检查哪个数据需要添加或者删除,这样就减少了DOM操作,也就提升了性能。

    获取差异化DOM的思路

    前面我们提到了,我们只需要通过对比变化前的数据变化后的数据来获得差异化数据,然后将该数据重新渲染即可。

    那么,具体的思路是如何的?

    我们都知道,ViewModel的变化会反映到View层上,我们可以通过对比新ViewModel和旧的ViewMode 来获得发生改变的位置,从而获取需要View需要改变的位置。

    ViewModel从某个角度来说,就是描述View层的一种数据结构。了解AST的同学会清楚,可以通过数据结构来生成一种DOM对象树结构。

    我们还是以前面的代码为例,模拟一下DOM树结构:

    //旧的dom对象
    let ulTree = {
      tagName: 'ul',
      attributes: [
        {id: 'root'}
      ],
      children: [
        {
          tagName: 'li',
          nodeText: '1',
          children: []
        }
      ]
    }
    

    这里我只写了一个li,但是通过这段代码,大家应该可以清楚所谓的DOM树结构是什么。

    list新增了一个元素,对应的会生成如下一种JS对象的DOM树结构:

    // 新的dom对象
    let ulTree = {
      tagName: 'ul',
      attributes: [
        {id: 'root'}
      ],
      children: [
        {
          tagName: 'li',
          nodeText: '1',
          children: []
        },
        {
          // 新增的
          tagName: 'li',
          nodeText: '2',
          children: []
        }
      ]
    }
    

    有了新旧两段JS对象来描述的DOM树结构,我们就可以通过对比这两个JS对象,获得一个差异化的数据,并且拿到该发生变化数据的位置,在该位置进行相应的DOM操作。避免产生其他没必要的操作。

    在这里,我个人对Virtual DOM有一个比较俗的理解:使用Virtual DOM的过程就好比我们生病了,去医院检查出什么病,直接对症下药,直接命中🎯。而不使用Virtual DOM的过程就好比我们不管什么病,直接吃一种包治百病的药💊,反正也能治好。

    核心实现

    前面我们用一些伪代码来介绍了VIrtual DOM的实现基本原理和概念。总的可以概括为三个步骤:1. 创建原始Virtual DOM;2.对比原始(旧)Virtual DOM 和用户操作所生成的新的Virtual DOM,生成差异化Virtual DOM;3.将差异化Virtual DOM渲染到页面上。

    我们分别来看这3个步骤如何去实现。

    创建Virtual DOM

    创建Virtual DOM即把一段HTML字符串解析成一个可以描述它的JS对象,也就是类似一个DOM树结构的对象,前面我们也已经介绍了。但是,如何去创建Virtual DOM是一件很重的事情。一般的,我们可以能想到直接去遍历HTML去实现一个Virtual DOM结构。但是这样是错误的。

    我们要清楚的是,Virtual DOM的优点就是减少DOM的操作,而我们如果直接去通过DOM API去扫描HTML代码,这本身就会使用的DOM的读取操作,很显然违背了我们的原则。

    所以,我们选择另外一种方式,将HTML代码写成JS中的一个字符串,通过某种解析规则去解析这段HTML的字符串,在解析的过程中,我们就可以生成Virtual DOM。

    🌰🌰🌰

    let htmlString = `
      <ul id="root">
        <li>1</li>
      </ul>
    `;
    // 调用parse方法来解析这段字符串,比如通过正则。
    // parse方法逐个分析字符,
    // 将标签存入tagName, 
    // 将属性存入attributes,
    // 将子标签存入children,
    // 这样就会产生前面我们所描述的DOM树结构对象。
    // 整个过程中仅仅是对js字符串的操作
    let vdom = parse(htmlString); 
    

    简单来说,创建Virtual DOM往往就是将一段DOM描述字符串解析成VIrtual DOM对象的过程。

    接着,我们生成了Virtual DOM后,还没有交给浏览器去解析,目前仅仅是有一个JS对象。所以我们还要去渲染DOM,但是,我们不会直接丢给浏览器去解析,而是通过自己去解析Virtual DOM 去生成HTML元素。

    🌰🌰🌰

    // 接收一个Virtual DOM对象。
    function render(vdom) {
      let element = document.createElement(vdom.tagName); // 这也是React 外面只能有一个根节点的原因。
      let attribuest = vdom.attributes;
      // 遍历,设置根节点的dom属性
      for (let key in attribuets) {
        element.setAttribuets(key, attributes[key]);  
      }
      // 处理子元素
      let children = vdom.children || [];
      for (let child of children) {
        // 判断是文本节点还是元素节点
        let childNode = (typeof child === 'string') ? document.createTextNode(child) : render(child); 
        element.appendChild(childNode);
      }
      return element;
    }
    

    上面是一个简单的DOM渲染的逻辑处理,此时我们就获取了Virtual DOM 并且重新在页面上渲染出了对应的元素。

    获取差异化Virtual DOM

    前面我们也提到了通过对比来获取差异化Virtual DOM。这里涉及到一个算法,即多叉树算法。Virtual DOM的对比实际上就是对于多叉树结构的遍历算法。其中,又有广度优先算法和深度优先算法。我对这个算法不熟悉,大家可以自行去搜索学习。

    但是,基本的概念还是要理解的。

    假设我们两个Virtual DOM每个节点对应一个唯一的字母,节点顺序分别使用深度优先遍历算法表示为:
    1.ABCEFGHDIJ
    2.AKLMBEFCGHDIJ

    [图片上传中...(2.png-17cc3b-1524548971902-0)]
    2.png

    通过对比,很容易发现我们需要在AB之间插入KLM节点,然后在通过KLM的关系得知,我们只需要插入K节点即可。

    在对比过程中,我们要进行一系列的记录,比如发生的类型、位置,进行相应的增删改查操作等。

    渲染差异化Virtual DOM

    前面通过对比获取到了差异化Virtual DOM,拿到了类型和位置,我们直接通过DOM操作将内容渲染到对应位置上即可。

    总结

    Virtual DOM 是JS对象,对DOM树结构的一种抽象表现。

    Virtual DOM最大的优势就是通过减少对DOM的操作次数,来提高交互性能和效率。但是我们也要清楚,其本质还是操作DOM,只不过仅仅是操作那些发生变化的DOM,减少了多余的操作。

    基本的实现步骤为以下3点:

    • 初始化Virtual DOM;
    • 对比初始化(旧)的Virtual DOM和新的Virtual DOM 来生成差异化 Virtual DOM;
    • 渲染差异化Virtual DOM。

    谈谈自己的目标

    自己毕业也将近一年了,回顾自己刚开始接触自己前端的时候,发现确实成长了不少,特别是在毕业之后到现在,这段时间相较之前的成长是最快的,自己的知识体系丰富了不少。但是,当知识体系丰富了之后,发现自己需要掌握的还有很多。

    总的目标方向不能变,就是提升自己的技术水平,当技术水平达到baseline后,开始寻求某一方面的突破,并提升自己的领导和业务能力

    当前目标

    自己在毕业的时候和同学注册了公司,但是一年下来,感觉对自己的技术成长有局限性。所以准备辞去当前公司工作,出去历练一下。所以当前目标,就是为了近期面试做准备。

    • 掌握Vue原理
    • 夯实JS基础知识
    • 复习一下基本的WEB安全和HTTP协议
    • 刷一些面试题

    短期目标

    在2-3年内,全面掌握这次课程大纲中涉及的内容,进入一线互联网公司,并慢慢成长为一个可以领导小型项目leader,或者具备一个leader的能力,对前端知识体系有一个全面掌握,并在个别方便有突破。

    所以,成为一个leader,除了技术要达标外,还要有业务和领导能力。

    总的来说,短期目标就是将自己前端技术水平提升到一个高度,具体来说:除了基本的前端语言外,需要掌握HTTP、WEB安全、算法、部分计算机原理等。另外,还要提升自己的业务能力和领导能力。

    长期目标

    长期目标,对于我来说,在技术水平达到一个高度后,选择重新创业的可能性会更大。但是在自己成长过程中,自己最基本的技术仍然要时刻学习,技术没有尽头的,况且前端行业发展迅速,知识更新快。

    另外,继续坚持每周写周报的习惯。

    文坚老师给出的这段话我觉得很好,这也是成长过程中需要的:

    • 知道敌人在哪。也就是业务问题在哪里?
    • 知道敌人从哪里来。也就是这个问题可以用什么来解决?是用技术来解决?还是用流程解决?还是要靠协同合作解决?
    • 知道怎么去伏击敌人,自己的优势在哪里。也就是根据自己的情况(资源、协同能力、影响力等)去制定最有效的方案(可能是技术,也可能是流程,亦可能是协同)
    • 知道在哪里避战,哪里主动出击,避其锋芒。知道哪些东西可以放弃,哪些东西必须解决,有重点有主线。
    • 知道怎么提升小队的战斗力。也就是,怎么培养人,怎么用事情来锻炼人
    • 知道怎么去请战功。也就是打了小规模胜战要让别人知道,例如让主管知道,让合作伙伴知道,让竞争对手知道,来建立跟你打战有肉吃的感受,提升小队凝聚力
    • 如有可能,甚至知道怎么联动友军去夹击敌人。也就是怎么跨团队合作,怎么通过合作来达成局部战略目的。

    相关文章

      网友评论

          本文标题:Virtual DOM

          本文链接:https://www.haomeiwen.com/subject/npvdlftx.html