虚拟 DOM
虚拟DOM :virtual dom(以下简称vdom,是vue和react的核心),使用比较简单。
一,vdom是什么,为何会存在vdom
1,什么是vdom:用js模拟DOM结构,DOM操作非常‘昂贵’,DOM变化的对比,放在JS层来做(图灵完备语言),提高重绘性能
需求:根据给出的数据,将该数据展示成一个表格, 随便修改一个信息, 表格也跟着修改,下面使用jquery实现demo:
<div id="container"></div>
<button id="btn-change">change</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
var data = [{
name: '张三',
age: '20',
address: '北京'
}, {
name: '李四',
age: '21',
address: '上海'
}, {
name: '王五',
age: '22',
address: '广州'
}]; // 渲染函数
function render(data) {
var $container = $('#container');
// 清空容器,重要
$container.html('');
// 拼接table
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 = '深圳';
// re-render 再次渲染
render(data)
}) // 页面加载完立刻执行(初次渲染)
render(data)
遇到的问题:DOM操作是昂贵的,改动后,整个container容器都重新渲染了一遍,相当于‘推倒重来’,如果项目复杂,非常影响性能
dom操作的属性是非常多的,非常复杂,操作很昂贵,所以,尽量用js代替操作,例:
var div = document.createElement('div');
var item, result = '';
for(item in div) {
result += '|' + item;
}
console.log(result);

vdom可以解决这个问题
二,vdom如何应用,核心API是什么
1,介绍snabbdom
var vnode = h('ul#list', {}, [
h('li.item', {}, 'Item 1'),
h('li.item', {}, 'Item 2')
])
{
tag: 'ul',
attrs: {
id: 'list'
},
children: [{
tag: 'li',
attrs: { className: 'item' },
children: ['Item 1']
}, {
tag: 'li',
attrs: { className: 'item' },
children: ['Item 2']
}]
}
<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<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.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.min.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) // 模拟改变
var btnChange = document.getElementById('btn-change');
btnChange.addEventListener('click', function() {
var newVnode = h('ul#list', {}, [
h('li.item', {}, 'Item 1'),
h('li.item', {}, 'Item 222'),
h('li.item', {}, 'Item 333'),
]);
patch(vnode,newVnode);
})
</script>
</body> </html>
2,重做之前的demo
<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<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.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.min.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 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) {
// re-render
patch(vnode,newVnode)
} else {
// 初次渲染
patch(container,newVnode)
}
// 存储当前vnode结果
vnode = newVnode; } // 初次渲染
render(data) var btnChange = document.getElementById('btn-change');
btnChange.addEventListener('click', function() {
data[1].age = 30;
data[2].address = '深圳';
// re-render
render(data)
}) </script>
</body> </html>
3,核心API
h('标签名',{...属性...},[...子元素...]) //多个子元素
h('标签名',{...属性...},'...') //只有一个子元素
patch(container,vnode) //初次渲染,会把外层容器替代掉
patch(vnode,newVnode) //re-render
三,介绍diff算法(vdom核心算法)
1,vdom为何用diff算法
diff 是linux的基础命令,可以比较两个文本文件的不同 git diff xxx; vdom中应用diff算法是为了找出需要更新的节点
比如新建两个文本文件,log1.txt log2.txt
diff log1.txt log2.txt
diff在线对比:http://tool.oschina.net/diff
使用vdom原因:DOM操作是昂贵的,因此尽量减少DOM操作
找出本次DOM必须更新的节点来更新,其他的不更新
这个找出的过程,就需要diff算法 找出前后两个vdom的差异
2,diff算法的实现流程
vdom核心函数:h生成dom节点,patch函数-进行对比和渲染的
patch(container,vnode) 初次渲染,会把外层容器替代掉
patch(vnode,newVnode) re-render
3,如何用vnode生成真是的dom节点
diff实现:
1,patch(container,vnode)
2,patch(vnode,newVnode)
核心逻辑:createElement 和 updateChildren
// patch(container,vnode)
function createElement(vnode) {
var tag = vnode.tag;
var attrs = vnode.attrs || {};
var children = vnode.children || [];
if (!tag) {
return null;
}
// 创建真实的DOM元素
var elem = document.createElement(tag);
// 属性
var attrName;
for (attrName in attrs) {
if (attrs.hasOwnProperty(attrName)) {
// 给elem添加属性
elem.setAttribute(attrName, attrs[attrName]);
}
}
// 子元素
children.forEach(function(childNode) {
// 递归调用 createElement 给elem添加子元素
elem.appendChild(createElement(childVnode)); //递归
})
// 返回真实的DOM元素
return elem;
} // patch(vnode,newVnode)
function updateChildren(vnode, newVnode) {
var children = vnode.children || [];
var newChildren = newVnode.children || []; // 遍历现有的children
children.forEach(function(child, index) {
var newChild = newChildren[index];
if (newChild == null) {
return;
}
if (child.tag === newChild.tag) {
// 两者tag一样 深层次对比
updateChildren(child, newChild);
} else {
// 两者tag不一样 替换
replaceNode(child, newChild)
}
})
} function replaceNode(vnode, newVnode) {
var elem = vnode.elem; //真实的DOM节点
var newElem = createElement(newVnode);
// 替换
}
虚拟 DOM的更多相关文章
- 虚拟dom与diff算法 分析
好文集合: 深入浅出React(四):虚拟DOM Diff算法解析 全面理解虚拟DOM,实现虚拟DOM
- React虚拟DOM浅析
在Web开发中,需要将数据的变化实时反映到UI上,这时就需要对DOM进行操作,但是复杂或频繁的DOM操作通常是性能瓶颈产生的原因,为此,React引入了虚拟DOM(Virtual DOM)的机制. 什 ...
- React的虚拟DOM
ReactJs的一大特点就是引进了虚拟dom(Virtual DOM)的概念.为什么我们需要Virtual DOM,Virtual DOM给我们带来了什么优势. 首先我们要了解一下浏览器的工作流. 当 ...
- react通过自己的jsx语法将两者放在一起通过虚拟dom来渲染
目前较为流行的react确实有很多优点,例如虚拟dom,单向数据流状态机的思想.还有可复用组件化的思想等等.加上搭配jsx语法和es6,适应之后开发确实快捷很多,值得大家去一试.其实组件化的思想一直在 ...
- 【虚拟DOM】√
深度剖析:如何实现一个 Virtual DOM 算法 为什么虚拟DOM更优胜一筹 新建树,渲染树,新建新树,对比树(算法),最少dom操作的渲染树
- React生命周期和虚拟DOM
一.虚拟DOM 1.React并不直接操作DOM,React中的render方法,返回一个DOM描述,React能够将这个DOM描述与内存中的表现进行比较,然后以最快的方式更新浏览器 2.React实 ...
- [深入react] 4.牛逼闪闪的虚拟DOM
React.createElement嵌套后的结果就是虚拟dom,虚拟dom听着很高端,其实就是一个json,类似: { type:'div', props:{ className:"box ...
- React虚拟DOM具体实现——利用节点json描述还原dom结构
前两天,帮朋友解决一个问题: ajax请求得到的数据,是一个对象数组,每个对象中,具有三个属性,parentId,id,name,然后根据这个数据生成对应的结构. 刚好最近在看React,并且了解到其 ...
- 实现一个简单的虚拟DOM
现在的流行框架,无论React还是Vue,都采用虚拟DOM. 好处就是,当我们数据变化时,无需像Backbone那样整体重新渲染,而是局部刷新变化部分,如下组件模版: <ul class=&qu ...
- 虚拟DOM详解
虚拟DOM简介 Virtual Dom可以看做一棵模拟了DOM树的JavaScript对象树,其主要是通过vnode,实现一个无状态的组件,当组件状态发生更新时,然后触发Virtual Dom数据的变 ...
随机推荐
- 机器翻译质量评测算法-BLEU
机器翻译领域常使用BLEU对翻译质量进行测试评测.我们可以先看wiki上对BLEU的定义. BLEU (Bilingual Evaluation Understudy) is an algorithm ...
- Web开发经验谈之F12开发者工具/Web调试[利刃篇]
引语:如今的整个Web开发行业甚至说整个软件开发行业,已经相当成熟,基本上已经很少找不到没有前人做过的东西了,或者换句话说,你想要实现的功能,你总能在某个地方搜索到答案,关键是你有没有这个时间精力去搜 ...
- IDEA的maven配置
刚接触maven的时候,也知道maven目录下有个setting文件可以设置远程maven库的地址,但自己实践的时候,发现setting文件的地址都被注释掉了,但是jar包还是能成功下载下来,那这个下 ...
- SQL查询去掉重复数据
本文主要总结数据库去掉重复数据的方法 去掉重复数据的方法: 第一种:distinct 根据单个字段去重,能精确去重: 作用在多个字段时,只有当这几个字段的完全相同时,才能去重: 关键字distinct ...
- ubuntu18.04 出现 Command 'ifconfig' not found 问题的解决办法
我们在虚拟主机中查看ip地址需要输入ifconfig,但是报以下错误: 系统提示我们安装 net-tools,当我们输入以下命令,即可安装完成. sudo apt-get install net-to ...
- [每天解决一问题系列 - 0005] WiX Burn 如何校验chained package的合法性
问题描述: 项目中使用Wix burn打包,内部包含了多个MSI.有时候会遇到如下错误 Error 0x80091007: Failed to verify hash of payload: Setu ...
- 在线画UML图的工具
工作需要在线画各种UML图,类图.协作图.用例图等等,调查了一些在线画UML图的工具,有的做的很好但要收费,例如:http://www.gliffy.com/,发现现在免费好用的是ProcessOn: ...
- Java工程师学习指南 中级篇
Java工程师学习指南 中级篇 最近有很多小伙伴来问我,Java小白如何入门,如何安排学习路线,每一步应该怎么走比较好.原本我以为之前的几篇文章已经可以解决大家的问题了,其实不然,因为我写的文章都是站 ...
- CentOS部署pyspider
0x00 环境 阿里云ECS云服务器 CPU:1核 内存:2G 操作系统:Centos 7.3 x64 地域:华北 2(华北 2 可用区 A) 系统盘:40G 0x01 安装依赖 yum instal ...
- Spring Boot 解决方案 - 配置
习惯优于配置 Spring Boot 项目的重要思想就是"习惯优于配置",这也是为什么该项目诞生的原因,让开发者免于 Spring 生态中各种项目的配置.尽管如此,但项目中完全零配 ...