vdom是vue和React的核心。
- vdom是什么?为何会存在vdom?
- vdom如何应用,核心API是什么
一、vdom是什么?
虚拟DOM,用JS模拟DOM结构。DOM变化的对比,放在JS层来做(图灵完备语言)。提高重绘性能。
1、什么是图灵完备语言?
就是可以实现逻辑、判断、算法的语言。
2、什么是vdom?
这是一个真实的DOM
<ul id="list">
<li class="item">Item 1</li>
<li class="item">Item 2</li>
</ul>
用JS来模拟上面的DOM,就是如下:
{
tag: 'ul',
attrs: {
id: 'list'
},
children: [
{
tag: 'li',
attrs: {className: 'item'}, //class是JS保留字,所以这里使用className
children: ['Item 1']
},{
tag: 'li',
attrs: {className: 'item'},
children: ['Item 2']
}
]
}
假设DOM发生了变化,我们在真实的DOM结构中,我们要进行DOM操作。而在虚拟的DOM结构中,我们只需要修改不一样的地方即可。为什么这样做,因为浏览器在进行DOM操作的时候消耗很大,而进行JS操作,操作很多次也没什么关系。DOM操作是最昂贵的,现在浏览器执行JS速度非常快。所以说用虚拟DOM。
二、设计场景(为什么会存在vdom)
将数据展示成一个表格。随便修改一个信息,表格也跟着修改。数据如下:
[
{
name: '张三',
age: '20',
address: '北京'
},
{
name: '李四',
age: '21',
address: '上海'
},
{
name: '王五',
age: '22',
address: '广州'
}
]
1、用jQuery实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="./jquery-1.12.4.min.js"></script>
</head>
<body>
<div id="container"></div>
<button id="btn-change">change</button>
<script>
var data = [
{
name: '张三',
age: '20',
address: '北京'
},
{
name: '李四',
age: '21',
address: '上海'
},
{
name: '王五',
age: '22',
address: '广州'
}
]
function render(data) {
var $container = $('#container')
$container.html('')
var $table = $('<table>')
$table.append($('<tr><td>name</td><td>age</td><td>address</td></tr>'))
data.forEach(function(item){
$table.append($('<tr><td>'+ item.name +'</td><td>' + item.age + '</td><td>' + item.address + '</td></tr>'))
})
//渲染到页面
$container.append($table)
}
$('#btn-change').click(function(){
data[1].age = 30
data[2].address = '深圳'
render(data)
})
render(data)
</script>
</body>
</html>
这样做的时候,每次点击click,table里面的所有内容都会重新渲染一次。DOM操作是非常昂贵的。这就是用jQuery实现的时候出现的问题。于是我们引出了vdom。
三、vdom如何应用?核心API是什么?
1、snabbdom?
h函数和pathch函数就是snabbdom的核心API,也就是vdom的核心API。
h函数就是定义一个虚拟DOM节点。
var vnode = h('ul#list', {}, [
h('li.item', {}, 'Item1'),
h('li.item', {}, 'Item2')
])
//对应的内容:
{
tag: 'ul',
attrs: {
id: 'list'
},
children: [
{
tag: 'li',
attrs: {className: 'item'},
children: ['Item 1']
},{
tag: 'li',
attrs: {className: 'item'},
children: ['Item 2']
}
]
}
patch函数
patch(container, vnode) //第一次渲染,container是一个真实的DOM节点,vnode是虚拟的DOM,将vnode放入到container中。
patch(vnode, newVnode) //DOM有所改变时,将新的newVnode渲染到vnode中,这个过程中将进行复杂的对比,只修改不同的地方
如下是用vdom实现的一个例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<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
var patch = snabbdom.init([
snabbdom_class,
snabbdom_props,
snabbdom_style,
snabbdom_eventlisteners
])
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)
//点击click,只修改了第二个li,新增了第三个li,第一个li并没有重新渲染。
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>
</html>
三、用vnode实现前面的设计场景如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<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
var patch = snabbdom.init([
snabbdom_class,
snabbdom_props,
snabbdom_style,
snabbdom_eventlisteners
])
var h = snabbdom.h
var data = [
{
name: '张三',
age: '20',
address: '北京'
},
{
name: '李四',
age: '21',
address: '上海'
},
{
name: '王五',
age: '22',
address: '广州'
}
]
//把表头也放在data中
data.unshift({
name:'姓名',
age: '年龄',
address: '地址'
})
var container = document.getElementById('container')
//渲染函数
var vnode
function render(data){
var newVnode = h('table',{}, data.map(function(item){
var tds = []
var i
for(i in item){
if(item.hasOwnProperty(i)){
tds.push(h('td', {}, item[i] + ''))
}
}
return h('tr',{},tds)
}))
if(vnode) {
patch(vnode, newVnode)
} else {
patch(container, newVnode)
}
vnode = newVnode
}
render(data)
var btnChange = document.getElementById('btn-change')
btnChange.addEventListener('click', function() {
data[1].age = 30
data[2].address = '深圳'
render(data)
})
</script>
</body>
</html>
四、vdom的核心API
- h('<标签名>',{...属性...},[...子元素...])
- h('<标签名>',{...属性...},'...')
- patch(container,vnode)
- patch(vnode,newVnode)