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操作带来的性能问题。

  • 相关阅读:
    《C# to IL》第一章 IL入门
    multiple users to one ec2 instance setup
    Route53 health check与 Cloudwatch alarm 没法绑定
    rsync aws ec2 pem
    通过jvm 查看死锁
    wait, notify 使用清晰讲解
    for aws associate exam
    docker 容器不能联网
    本地运行aws lambda credential 配置 (missing credential config error)
    Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?
  • 原文地址:https://www.cnblogs.com/zhouyangla/p/6502356.html
Copyright © 2011-2022 走看看