虚拟DOM

了解浏览器加载html文件时需要做哪些事情(即浏览器的渲染过程),能够更好的帮助我们理解虚拟DOM。关于浏览器的渲染过程,可以参考我的另一篇博文https://blog.csdn.net/ty987654/article/details/78347390。浏览器的渲染过程大致可以分为5步:创建DOM树-----创建css规则树-----构建render树-----layout布局-----绘制render树。

为什么使用虚拟DOM?

当我们使用原生的js或jquery操作DOM时,浏览器会从构建DOM开始从头到尾执行一遍流程。

比如当在某一次操作时,需要更新10个DOM节点,理想状态下是一次性构建完成DOM树,然后再进行后续的DOM节点更新。但浏览器不会如此智能,当浏览器收到第1个更新DOM请求后,会立即执行该操作,但后续还有9次更新操作,所以浏览器会执行10次更新流程。如果浏览器在第1次更新时,计算出每个DOM节点的坐标值,接收第2次更新请求时,节点坐标值发生变化,那么第一次的计算相当于是无用功,白白浪费了资源与性能。

实现虚拟DOM:

虚拟DOM:使用js对象模拟DOM节点:页面的更新会反映在js对象上,操作js对象比实际上操作DOM的速度更快,更新完成后,将js对象映射成真实的DOM,然后由浏览器进行渲染。

真实的DOM节点:

<div id="real-container">
    <ul>
        <li class="item">Item 1</li>
        <li class="item">Item 2</li>
        <li class="item">Item 3</li>
    </ul>
</div>

使用js对象模拟DOM节点:页面的更新会反映在js对象上,操作js对象比实际上操作DOM的速度更快,更新完成后,将js对象映射成真实的DOM,然后由浏览器进行渲染。

element = {
    tagName: 'div',
    props: { id: '' },
    children: [
        {
            tagName: 'ul',
            props: {},
            children: [
                {
                    tagName: 'li',
                    props: {class: 'item'},
                    children: [Item1]
                },
                {
                    tagName: 'li',
                    props: {class: 'item'},
                    children: [Item2]
                },
                {
                    tagName: 'li',
                    props: {class: 'item'},
                    children: [Item3]
                },
            ]
        }
    ]
}

const tree = Element('div', { id: 'virtual-container' }, [
    Element('ul', {}, [
        Element('li', { class: 'item' }, ['Item 1']),
        Element('li', { class: 'item' }, ['Item 2']),
        Element('li', { class: 'item' }, ['Item 3']),
    ]),
]);

const root = tree.render();
document.getElementById('virtualDom').appendChild(root);

Element方法的实现:

function Element(tagName, props, children) {
    if (!(this instanceof Element)) {
        return new Element(tagName, props, children);
    }

    this.tagName = tagName;
    this.props = props || {};
    this.children = children || [];
    this.key = props ? props.key : undefined;

    let count = 0;
    this.children.forEach((child) => {
        if (child instanceof Element) {
            count += child.count;
        }
        count++;
    });
    this.count = count;
}
// tagName表示节点名,如div,props表示节点的属性,如class,children表示子节点

js对象映射成真实的DOM:

Element.prototype.render = function() {
    // 通过createElement创建元素
    const el = document.createElement(this.tagName);
    const props = this.props;
    
    // 将属性添加在元素上
    for (const propName in props) {
        setAttr(el, propName, props[propName]);
    }

    // 存在子元素递归调用创建子元素
    this.children.forEach((child) => {
        const childEl = (child instanceof Element) ? child.render() : document.createTextNode(child);
        // 通过appendChild方法添加子元素
        el.appendChild(childEl);
    });

    return el;
};

diff算法:

比较两棵树的差异,采用diff算法。两个树的完全的 diff 算法是一个时间复杂度为 O(n^3) 的问题。但是在前端当中,很少会跨越层级地移动DOM元素。所以 Virtual DOM 只会对同一个层级的元素进行对比,这样算法复杂度就会变成O(n)

 

虚拟DOM的核心可以总结为:

1、用js对象结构表示一个DOM树,然后基于对象树创建一个真正的DOM树,将DOM树插入到文档中

2、当状态变更的时候,重新构造一个新的对象树,将新的对象树与旧的对象树进行比较,记录它们的不同之处

3、将记录的不同之处应用在真正的DOM树上,实现视图的更新

 

虚拟DOM与真实DOM的区别:

1. 虚拟DOM不会进行回流与重绘操作。

2. 真实DOM频繁进行回流与重绘操作效率是非常低的。

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值