virtual dom 虚拟DOM

  • vdom是vue和React的核心,先讲哪个都绕不开它
  • vdom比较独立,使用也比较简单
  • 如果面试闻到vue和React和实现,免不了问vdom

vdom是什么?为何会存在vdom?

  • virtual dom,虚拟DOM
  • 用JS模拟DOM结构
  • 将DOM对比操作放在JS层来做,提高效率
  • 提高重绘性能

为何会存在:

  • DOM操作是“昂贵”的,非常耗费性能,js运行效率高
  • 尽量减少DOM操作,而不是“推倒重来”
  • 项目越复杂,影响就越严重
  • vdom即可解决这个问题

vdom

  • 有了一定复杂度,想减少计算次数比较难
  • 能不能把计算,更多的转移为JS计算?因为JS执行速度很快
  • vdom - 用JS模拟DOM结构
  • 新旧vnode对比,得出最小的更新范围,最后更新DOM

用js模拟dom结构

<div id="div1" class="container">
	<p>vdom></p>
	<ul style="font-size: 20px;">
		<li>a</li>
	</ul>
</div>
{
	tag: 'div',
	props: {
		className: 'container',
		id: 'div1'
	},
	children: [
		{
			tag: 'p',
			children: 'vdom'
		},
		{
			tag: 'ul',
			props: {
				style: 'font-size: 20px'
			},
			children: [
				{
					tag: 'li',
					children: 'a'
				}
			]
		}
	]
}
  • Vue3.0重写了vdom的代码,优化了性能
  • 但vdom的基本理念不变

vdom如何应用,核心API是什么

使用snabbdom

<body>
    <div id="container"></div>
    <button id="btn-change">change</button>

    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-class.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-props.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-style.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-eventlisteners.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.1/h.js"></script>
    <script>
        var snabbdom = window.snabbdom

        // 定义patch
        var patch = snabbdom.init([
            snabbdom_class,
            snabbdom_props,
            snabbdom_style,
            snabbdom_eventlisteners
        ])

        // 定义h
        var h = snabbdom.h

        var container = document.getElementById('container')

        // 生成vnode
        var vnode = h('ul#list', {}, [
            h('li.item', {}, 'Item 1'),
            h('li.item', {}, 'Item 2')
        ])

        patch(container, vnode)

        document.getElementById('btn-change').addEventListener('click', function(){
            var newVnode = h('ul#list', {}, [
                h('li.item', {}, 'Item 1'),
                h('li.item', {}, 'Item B'),
                h('li.item', {}, 'Item 3')
            ])
            patch(vnode, newVnode)
        })
        
    </script>
</body>

介绍一下diff算法

  • 什么是diff算法?
    (1)diff即对比,是一个广泛的概念,如linux diff命令、git diff等
    (2)两个js对象也可以做diff,如 https://github.com/cujojs/jiff
    (3)两棵树做diff,如这里的vdom diff
    (4)树diff的时间复杂度为O(n^3)
    a. 第一,遍历tree1;第二,遍历tree2
    b. 第三,排序
    c. 如果树中有1000个节点,要计算1亿次,算法不可用
    (5)优化时间复杂度到O(n)
    a. 只比较同一层级,不跨级比较
    b. tag不相同,则直接删掉重建,不再深度比较
    c. tag和key,两者都相同,则认为是相同节点,不再深度比较
  • vdom为何用diff算法?
    (1)DOM操作是“昂贵”的,因此尽量减少DOM操作
    (2)找出本次DOM必须更新的节点来更新,其他的不更新
    (3)这个“找出”的过程,就需要diff算法
    (4)diff算法是vdom中最核心、最关键的部分
    a. patchVnode
    b. addVnodes、removeVnodes
    c. updateChildren(key的重要性)
  • diff算法实现流程
    (1)patch(container, vnode)
    (2)patch(vnode, newVnode)
// patch(container, vnode)情况
function createElement(vnode){
    var tag = vnode.tag
    var attrs = vnode.attrs || {}
    var children = vnode.children || []
    if(!tag){
        return null
    }

    // 创建元素
    var elem = document.createElement(tag)

    // 属性
    var attrName
    for(attrName in attrs){
        if(attrs.hasOwnProperty(attrName)){
            // 给elem添加属性
            elem.setAttribute(attrName, attrs[attrName])
        }
    }

    children.forEach(childVnode => {
        // 给elem田间子元素
        elem.appendChild(createElement(childVnode))  // 递归
    })

    // 返回真实的DOM元素
    return elem
}

// 数据
{
	tag: 'ul'
	attrs: {id: 'list'},
	children: [
		{
			tag: 'li',
			attrs: {className: 'item'},
			children: ['Item 1']
		}, {
			tag: 'li',
			attrs: {className: 'item'},
			children: ['Item 2']
		}
	]
}
// patch(vnode, newVnode)情况
function updateChildren(vnode, newVnode){
	var children = vnode.children || []
	var newChildren = newVnode.children || []
	
	// 遍历现有的children
	children.forEach(function(child, index){
		var newChild = newXChildren[index]
		if(newChild == null){
			return
		}
		if(child.tag === newChild.tag){
			// 两者tag一样
			updateChildren(child, newChild)
		} else {
			replaceNode(child, newChild)
		}
	})
}

核心API:

  • h函数
  • vnode
  • patch函数
  • diff
  • key

vdom存在的价值:
数据驱动视图,控制DOM操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值