zoukankan      html  css  js  c++  java
  • 全面理解虚拟DOM(1)

    最近一两年前端最火的技术莫过于 reactjs,angularJS,vuejs,即便你没用过也可能听过,像ReactJS由业界顶尖的互联网公司facebook提出,其本身有很多先进的设计思路,比如页面UI组件化、虚拟DOM等。本文将带你解开虚拟DOM的神秘面纱,不仅要理解其原理,而且要实现一个基本可用的虚拟DOM。

    1.为什么需要虚拟DOM

           DOM是很慢的,其元素非常庞大,页面的性能问题鲜有由JS引起的,大部分都是由DOM操作引起的。如果对前端工作进行抽象的话,主要就是维护状态和更新视图;而更新视图和维护状态都需要DOM操作。其实近年来,前端的框架主要发展方向就是解放DOM操作的复杂性。

           在jQuery出现以前,我们直接操作DOM结构,这种方法复杂度高,兼容性也较差;有了jQuery强大的选择器以及高度封装的API,我们可以更方便的操作DOM,jQuery帮我们处理兼容性问题,同时也使DOM操作变得简单;但是聪明的程序员不可能满足于此,各种MVVM框架应运而生,有angularJS、avalon、vue.js等,MVVM使用数据双向绑定,使得我们完全不需要操作DOM了,更新了状态视图会自动更新,更新了视图数据状态也会自动更新,可以说MMVM使得前端的开发效率大幅提升,但是其大量的事件绑定使得其在复杂场景下的执行性能堪忧;有没有一种兼顾开发效率和执行效率的方案呢?ReactJS就是一种不错的方案,虽然其将JS代码和HTML代码混合在一起的设计有不少争议,但是其引入的Virtual DOM(虚拟DOM)却是得到大家的一致认同的。

    2.理解虚拟DOM

    虚拟的DOM的核心思想是:对复杂的文档DOM结构,提供一种方便的工具,进行最小化地DOM操作。这句话,也许过于抽象,却基本概况了虚拟DOM的设计思想

    (1) 提供一种方便的工具,使得开发效率得到保证
    (2) 保证最小化的DOM操作,使得执行效率得到保证

    (1).用JS表示DOM结构

    DOM很慢,而javascript很快,用javascript对象可以很容易地表示DOM节点。DOM节点包括标签、属性和子节点,通过VElement表示如下。

    //虚拟dom,参数分别为标签名、属性对象、子DOM列表
    var VElement = function(tagName, props, children) {
        //保证只能通过如下方式调用:new VElement
        if (!(this instanceof VElement)) {
            return new VElement(tagName, props, children);
        }
    
        //可以通过只传递tagName和children参数
        if (util.isArray(props)) {
            children = props;
            props = {};
        }
    
        //设置虚拟dom的相关属性
        this.tagName = tagName;
        this.props = props || {};
        this.children = children || [];
        this.key = props ? props.key : void 666;
        var count = 0;
        util.each(this.children, function(child, i) {
            if (child instanceof VElement) {
                count += child.count;
            } else {
                children[i] = '' + child;
            }
            count++;
        });
        this.count = count;
    }

    通过VElement,我们可以很简单地用javascript表示DOM结构。比如

    var vdom = velement('div', { 'id': 'container' }, [
        velement('h1', { style: 'color:red' }, ['simple virtual dom']),
        velement('p', ['hello world']),
        velement('ul', [velement('li', ['item #1']), velement('li', ['item #2'])]),
    ]);

    上面的javascript代码可以表示如下DOM结构:

    <div id="container">
        <h1 style="color:red">simple virtual dom</h1>
        <p>hello world</p>
        <ul>
            <li>item #1</li>
            <li>item #2</li>
        </ul>   
    </div>

    同样我们可以很方便地根据虚拟DOM树构建出真实的DOM树。具体思路:根据虚拟DOM节点的属性和子节点递归地构建出真实的DOM树。见如下代码:

    VElement.prototype.render = function() {
        //创建标签
        var el = document.createElement(this.tagName);
        //设置标签的属性
        var props = this.props;
        for (var propName in props) {
            var propValue = props[propName]
            util.setAttr(el, propName, propValue);
        }
    
        //依次创建子节点的标签
        util.each(this.children, function(child) {
            //如果子节点仍然为velement,则递归的创建子节点,否则直接创建文本类型节点
            var childEl = (child instanceof VElement) ? child.render() : document.createTextNode(child);
            el.appendChild(childEl);
        });
    
        return el;
    }

    对一个虚拟的DOM对象VElement,调用其原型的render方法,就可以产生一颗真实的DOM树。

    vdom.render();

    既然我们可以用JS对象表示DOM结构,那么当数据状态发生变化而需要改变DOM结构时,我们先通过JS对象表示的虚拟DOM计算出实际DOM需要做的最小变动,然后再操作实际DOM,从而避免了粗放式的DOM操作带来的性能问题。

  • 相关阅读:
    方格取数+ 传纸条 noip2000 + noip2008 DP
    题解 P1103 【书本整理】
    CF212D 【Cutting a Fence】
    CF339D 【Xenia and Bit Operations】
    旅行 NOIP2018 luogu P5022
    CodeFores 集合
    战略游戏 SDOI2018 圆方树 + 树上倍增求点权和
    树网的核 NOIP 2007 luogu P1099
    P2341 [USACO03FALL][HAOI2006]受欢迎的牛 G
    NOIP 2017 P3959 宝藏 (状态压缩DP板子)
  • 原文地址:https://www.cnblogs.com/zhouyangla/p/6502356.html
Copyright © 2011-2022 走看看