- vdom是什么,为何会存在vdom
- vdom如何应用,核心API是什么
- 介绍一下diff算法(vdom80%依赖diff算法)
★什么是vdom,为何会存在vdom
设计一个需求场景:将data数据展示成一个表格,随便修改一个信息,表格也跟着修改
<body>
<div id="container"></div>
<button id="change">修改</button>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
var data=[
{
name:"张三",
age:"20",
address:"北京"
},
{
name:"李四",
age:"20",
address:"上海"
},
{
name:"王五",
age:"20",
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);
};
//点击修改信息
$('#change').click(function(){
data[1].age=30;
data[2].address='深圳';
render(data);
});
//初次渲染
render(data);
</script>
</body>
用jQuery实现的不足:
- DOM操作是昂贵的,JS运行效率高
- 尽量减少DOM操作,而不是推到重来
- 项目越复杂,影响就越严重
- vdom即可解决该问题
浏览器自己创造的实际DOM元素中有许多属性和方法,但用JS模拟的虚拟DOM结构很简单。JS模拟的虚拟DOM能尽量减少实际DOM的操作,提高性能。
JS模拟的虚拟DOM结构如下:
回答提出的问题(什么是vdom,为何会存在vdom):
- virtual dom,虚拟DOM
- 是用JS模拟DOM结构(原因:为了提高性能,我们需要用JS找出哪些DOM要操作,哪些DOM不用操作,这需要逻辑运算,即需要图灵完备的语言,前端即JS)
- 将DOM对比操作放在JS层,提高效率
★vdom如何应用,核心API是什么
说到vdom,就会想到snabdom,vue2.0里面的虚拟dom就是通过snabdom来实现的。
snabdom部分源码:
vdom是JS模拟的dom元素,vnode是JS模拟的node节点。
从snabdom的源码中就可以看到,实现虚拟dom最核心的API是两个函数:h函数、patch函数。
h函数可以生成JS模拟的DOM结构,h函数的三个参数分别对应用JS模拟的DOM节点里的tag,attrs,children,例:
h函数的第一个参数是’tagName.className’或者’tagName#Id’,第二个参数为一个对象,里面可以是事件或者样式或者属性值那些,第三个参数可以为一个,也可以为多个(数组)。
介绍snabdom里的patch函数:
patch函数有两个用法:
- patch(container,vnode); 意思是将vnode表示的虚拟DOM节点全部塞到container中。
- patch(vnode,newVnode); 第一个参数是旧的vnode,第二个参数为新的vnode,意思是将新旧vnode进行对比,没改动的地方不变,只改动变了的地方。
重写前面将数据渲染成表格的例子:
<body>
<div id="container"></div>
<button id="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 type="text/javascript">
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 () {
// 生成 newVnode
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>
在调试窗口的Element中可以看到,只有修改了的DOM重新渲染了,而不是推到重来,减少了DOM操作,提高了性能。
问题解答:
关于vdom如何应用,已经在上面的demo中演示过了。
vdom的核心API总结:
- h(‘标签名’,{属性},[子元素]); 多个子元素可以这样写
- h(‘标签名’,{属性},‘子元素’); 一个子元素可以这样写
- patch(container,vnode);
- patch(vnode,newVnode);
★介绍一下diff算法
1.什么是diff算法
2.去繁就简
3.diff算法在vdom中干什么
4.diff算法的实现流程
什么是diff算法:
diff是linux中的命令,用于找出两个文本文件的异同(diff log1.txt log2.txt )
git diff 是提交代码时与上一版本的对比。
所以diff不是vdom独创的算法(代码对比,一直存在),vdom中使用diff算法是为了找出需要需要更新的节点(只是对象变成了JS对象)。
diff算法的实现过程中有四个函数:
- patch(container,vnode);
- patch(vnode,newVnode);
- createElement(vnode);
- updateChildren(vnode,newVnode);
核心逻辑在 createElement(vnode);和updateChildren(vnode,newVnode);函数中
去繁就简
diff算法非常复杂,实现难度很大,源码量很大,去繁就简,总结核心流程
diff算法在vdom中干什么
- DOM操作是昂贵的,因此尽量减少DOM操作
- 需要找到本次DOM必须更新的节点来更新,其他的不更新
- 找出前后两个vdom之间差异的过程就需要diff算法
- 和diff算法在其他方面的应用对比,它在vdom中的应用只是文本对象变成了节点元素
diff算法的实现流程
- patch(container,vnode); 虚拟DOM变成实际DOM,即第一个patch的实现
- patch(vnode,newVnode); 对比更新DOM节点,即第二个patch函数的实现
那怎么由模拟的DOM结构生成实际的DOM结构呢 patch(container,vnode)
patch(vnode,newVnode)的实现
模拟DOM中的节点必须和真实的DOM对应上,因为找出区别更新的时候,必须得知道更新在哪个地方,所以vdom和真实dom必须有对应关系
对比vnode和newVnode
对比vnode和newVnode发现,第二个li元素不同,且增加了第三个li元素
对比出来不同后,它的实现是这样的
tag一样就直接递归对比子元素,不一样就直接替换
diff的实现过程总结:
- patch(container,vnode);
- patch(vnode,newVnode);
- createElement(vnode);
- updateChildren(vnode,newVnode);