什么是vdom? 为何会存在Vdom?
- 用 js 模拟 DOM 结构。
- DOM变化的对比放在js层来做。
- 提高重绘性能。
真实DOM:
<ul id="list">
<li class="item">item1</li>
<li class="item">item2</li>
</ul>
复制代码
js模拟:
{
tag: 'ul',
attrs: {
id: 'list'
},
children: [
{
tag: 'li',
attrs: {className: 'item'},
children: ['item1']
},
{
tag: 'li',
attrs: {className: 'item'},
children: ['item2']
}
]
}
复制代码
举一个栗子:
设计一个数据表格,点击按钮更改数据。
dom 操作的写法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="container"></div>
<button id="btn-change">数据更改</button>
</body>
<script src="./jquery.min.js"></script>
<script>
var data = [
{
name: 'test1',
age: '23',
address: 'shanghai'
},
{
name: 'test2',
age: '24',
address: 'beijing'
},
{
name: 'test3',
age: '25',
address: 'chengdu'
}
]
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);
}
render(data);
$('#btn-change').on('click', function () {
data[1].name = 'yezhiwei';
data[2].age = '30';
render(data);
})
</script>
</html>
复制代码
vdom如何应用,核心API是什么?
-
snabbdom介绍
-
核心api:
h(),patch()
先看一个栗子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="container"></div>
<button id="btn-change">change</button>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-class.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-props.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-style.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-eventlisteners.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/h.min.js"></script>
<script>
const snabbdom = window.snabbdom;
//定义patch函数
const patch = snabbdom.init([
snabbdom_class,
snabbdom_props,
snabbdom_style,
snabbdom_eventlisteners
]);
//定义h
const h = snabbdom.h;
const 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(){
//生成新的vnode
var newVnode = h('ul#list', {}, [
h('li.item', {}, 'item 1'),
h('li.item', {}, 'item b'),
h('li.item', {}, 'item 3')
])
patch(vnode, newVnode);
})
</script>
</html>
复制代码
改写上面dom处理的例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="container"></div>
<button id="btn-change">change</button>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-class.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-props.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-style.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-eventlisteners.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/h.min.js"></script>
<script>
const snabbdom = window.snabbdom;
//定义patch函数
const patch = snabbdom.init([
snabbdom_class,
snabbdom_props,
snabbdom_style,
snabbdom_eventlisteners
]);
//定义h
const h = snabbdom.h;
const container = document.getElementById('container');
const btnChange = document.getElementById('btn-change');
var data = [
{
name: 'test1',
age: '23',
address: 'shanghai'
},
{
name: 'test2',
age: '24',
address: 'beijing'
},
{
name: 'test3',
age: '25',
address: 'chengdu'
}
]
data.unshift({
name: '姓名',
age: '年龄',
address: '地址'
})
var vnode;
var newVnode;
render(data);
function render(data){
newVnode = h('table', {} ,data.map(function(item){
var tds = [];
for(var 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;
}
btnChange.addEventListener('click', function(){
data[1].name = 'yezhiwei';
data[2].age = '100';
render(data);
})
</script>
</html>
复制代码
打开控制台观察下两种方法的DOM变化时的闪烁情况,第二种方法明显优于第一种方法,只修改了变化的部分。
diff算法
为何使用diff算法,一句话来说明的话:
因为DOM操作比较昂贵,想要减少DOM的操作就必须找出必要更新的节点,而找出的过程就称为----diff算法。
模拟 patch(container, vnode);
function createElement(vnode){
var tag = vnode.tag,
attrs = vnode.attrs || {},
children = vnode.children || []
if(!tag){
return null
}
var elem = document.createElement(tag);
for(var attr in attrs){
if(attrs.hasOwnProperty(attr)){
elem.setAttribute(attr, attrs[attr]);
}
}
children.forEach(function(children) {
elem.appendChild(createElement(children));
})
return elem
}
复制代码
模拟patch(vnode, newVnode)
function updateChildren(vnode, newVnode){
var children = vnode.children || [];
var newChildren = newVnode.children || [];
children.forEach(function(child, index){
var newChild = newChildren[index];
if(!newChild){
return false
}
if(child.tag === newChild.tag){
updateChildren(child, newChild)
}else{
replaceNode(child, newChild)
}
})
}
复制代码