关于VirtualDom的知识点

记录一个前端大神的笔记,逐步学习 

原文 github.com/livoras/blo…

故事

作为React的核心概念之一,virtual dom从某种意义上改变了前端的现有体系。大部分人把virtual dom当做一种性能提升策略,因为它可以有效的减少直接操作DOM带来的消耗,从而提升脚本的执行效率。但是实际上,virtual dom并不完全是避免DOM操作的消耗,有的情况下,直接操作DOM可能来的更快,而virtual dom的真正意义,在于把直接的DOM操作和js编程,提炼成一个抽象层,virtual dom是对js操作DOM的抽象,对于今后的前端编程而言,developer无需再直接操作DOM,而是通过virtual dom去操作,这是它真正的意义。

VirtualDom真的快吗

VirtualDom最常被提及的优势就是它的效率高,原因是对js对象的操作要比对原生dom的操作要快很多,有了虚拟dom,我们更新视图前可以比较两颗虚拟dom树的差异,只更新差异部分,减少了对真正dom节点的操作,所以提高了效率。 然而事实上是不一定的,我们必须知道建立虚拟dom树本身和比较差异都是需要消耗时间的,如果不采用virtualdom,我们更新视图一般用innerHtml方法。这两种方法的更新时间可以粗略表示如下:

innerHtml:渲染成htmlstring + 重新创建所有dom

VirtualDom:渲染成VirtualDom + 比较新旧Dom树差异 + 必要dom更新 

所以两者的快慢实际取决于需要渲染的界面大小和更新部分的多少,可以看出innerHtml这种重新创建所有的dom的方式在只有几行数据改变时可能会造成大量浪费,所以从整体来看VirtualDom更优,但如果只是渲染静态界面,则innerHtml反而更快。

VirtualDom实现

一个简单VirtualDom机制,只需分为3个部分,第一步是建立VirtualDom树,第二步是设计比较新旧两个树之间差异的算法,最后一步是将差异更新到真正的视图部分。

创建VirtualDom树 

一般情况下我们可以将DOM树表示为一个JavaScript的Object对象,如下有一颗DOM树

<ul id='list'>
    <li class='item'>Item 1</li>
    <li class='item'>Item 2</li>
    <li class='item'>Item 3</li>
</ul>复制代码

该DOM树对应的JS对象如下:

var element = {
    tagName: 'ul', // 节点标签名
    props: { // DOM的属性,用一个对象存储键值对
        id: 'list'
    },
    children: [ // 该节点的子节点
        {tagName: 'li', props: {class: 'item'}, children: ["Item 1"]},
        {tagName: 'li', props: {class: 'item'}, children: ["Item 2"]},
        {tagName: 'li', props: {class: 'item'}, children: ["Item 3"]},
    ]
}复制代码

两相比较,我们可以发现,我们将DOM中的任一元素表示为:

{ type: ‘…’, props: { … }, children: [ … ] }复制代码

而DOM中的纯文本节点会被表示为普通的JavaScript中的字符串。不过这还是一个简单的DOM树,如果是一个较大型的树,我们就需要一个辅助函数来构造结构

function Element(tagName,props,children){
    this.tagName=tagName
    this.props=props
    this.children=children
}复制代码

我们用上面的构造函数来完成DOM数

const ul = new Element('ul',{id:'list'},[
    new Element('li',{class:'item'},['item 1']),
    new Element('li',{class:'item'},['item 1']),
    new Element('li',{class:'item'},['item 1'])
])复制代码

我们再来编写一个方法,来真正的渲染DOM

Element.prototype.render=function(){
    // 根据tagName构建
    var el = document.createElement(this.tagName)
    var props = this.props
    // 设置节点的DOM属性
    for (var propName in props) {
        var propValue = props[propName]
        el.setAttribute(propName, propValue)
    }

    var children = this.children || []

    children.forEach(function (child) {
        var childEl = (child instanceof Element)
            ? child.render() // 如果子节点也是虚拟DOM,递归构建DOM节点
            : document.createTextNode(child) // 如果字符串,只构建文本节点
        el.appendChild(childEl)
    })

    return el
}复制代码

这时,我们再把ul插入到真正的节点中:

const rootEl = ul.render()
document.body.appendChild(rootEl)复制代码

比较两棵虚拟DOM树的差异

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值