虚拟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的更多相关文章

  1. 虚拟dom与diff算法 分析

    好文集合: 深入浅出React(四):虚拟DOM Diff算法解析 全面理解虚拟DOM,实现虚拟DOM

  2. React虚拟DOM浅析

    在Web开发中,需要将数据的变化实时反映到UI上,这时就需要对DOM进行操作,但是复杂或频繁的DOM操作通常是性能瓶颈产生的原因,为此,React引入了虚拟DOM(Virtual DOM)的机制. 什 ...

  3. React的虚拟DOM

    ReactJs的一大特点就是引进了虚拟dom(Virtual DOM)的概念.为什么我们需要Virtual DOM,Virtual DOM给我们带来了什么优势. 首先我们要了解一下浏览器的工作流. 当 ...

  4. react通过自己的jsx语法将两者放在一起通过虚拟dom来渲染

    目前较为流行的react确实有很多优点,例如虚拟dom,单向数据流状态机的思想.还有可复用组件化的思想等等.加上搭配jsx语法和es6,适应之后开发确实快捷很多,值得大家去一试.其实组件化的思想一直在 ...

  5. 【虚拟DOM】√

    深度剖析:如何实现一个 Virtual DOM 算法 为什么虚拟DOM更优胜一筹 新建树,渲染树,新建新树,对比树(算法),最少dom操作的渲染树

  6. React生命周期和虚拟DOM

    一.虚拟DOM 1.React并不直接操作DOM,React中的render方法,返回一个DOM描述,React能够将这个DOM描述与内存中的表现进行比较,然后以最快的方式更新浏览器 2.React实 ...

  7. [深入react] 4.牛逼闪闪的虚拟DOM

    React.createElement嵌套后的结果就是虚拟dom,虚拟dom听着很高端,其实就是一个json,类似: { type:'div', props:{ className:"box ...

  8. React虚拟DOM具体实现——利用节点json描述还原dom结构

    前两天,帮朋友解决一个问题: ajax请求得到的数据,是一个对象数组,每个对象中,具有三个属性,parentId,id,name,然后根据这个数据生成对应的结构. 刚好最近在看React,并且了解到其 ...

  9. 实现一个简单的虚拟DOM

    现在的流行框架,无论React还是Vue,都采用虚拟DOM. 好处就是,当我们数据变化时,无需像Backbone那样整体重新渲染,而是局部刷新变化部分,如下组件模版: <ul class=&qu ...

  10. 虚拟DOM详解

    虚拟DOM简介 Virtual Dom可以看做一棵模拟了DOM树的JavaScript对象树,其主要是通过vnode,实现一个无状态的组件,当组件状态发生更新时,然后触发Virtual Dom数据的变 ...

随机推荐

  1. Git 常用命令备份

    继上次保存了git 多个key共存配置(https://www.cnblogs.com/xiaochangwei/p/9155195.html)后,今天把常见的git命令备份下(最近我的云笔记账号经常 ...

  2. Spring MVC 后端获取前端提交的json格式字符串并直接转换成control方法对应的参数对象

    场景: 在web应用开发中,spring mvc凭借出现的性能和良好的可扩展性,导致使用日渐增多,成为事实标准,在日常的开发过程中,有一个很常见的场景:即前端通过ajax提交方式,提交参数为一个jso ...

  3. Spring Boot定制启动图案

    启动图案 Spring Boot在启动的时候会显示一个默认的Spring的图案,对应的类为SpringBootBanner. . ____ _ __ _ _ /\\ / ___'_ __ _ _(_) ...

  4. Spring Boot整合 Thymeleaf 模板引擎

    什么是Thymeleaf Thymeleaf是一款用于渲染XML.XHTML.HTML5内容的模板引擎.类似Velocity,FreeMaker模板引擎,它也可以轻易的与Spring MVC等Web框 ...

  5. Liferay7 BPM门户开发之21: 理解消息总线(Message Bus)体系

    Liferay Message Bus提供了松耦合的消息发送接收机制(生产和消费的设计模式),用于本地服务,不支持远程服务,支持集群. 主要用途: 两个或多个插件之间的通讯. 在事件中发送搜索索引,比 ...

  6. 【BJOI2019】删数 线段树

    题目大意:一个数列若能在有限次数内删空,则称这个数列可以删空,一次删除操作定义如下: 记当前数列长度为$k$,则删掉数列中所有等于$k$的数. 现在有一个长度为$n$的数列$a$,有$m$次修改操作, ...

  7. 剑指offer【01】- 二维数组中的查找(Java)

    在经历了春招各大公司的笔试题和面试官的血虐之后,决定要刷一些算法题了,不然连面试机会都没有. 而应对笔试和面试,比较出名的就是剑指offer的题目和LeetCode的题目了.剑指offer应对面试中的 ...

  8. selenium的chromedriver对应的chrome版本

    chromedriver下载地址:http://chromedriver.storage.googleapis.com/index.html chromedriver版本 支持的Chrome版本 v2 ...

  9. Spring Cloud OAuth2.0 微服务中配置 Jwt Token 签名/验证

    关于 Jwt Token 的签名与安全性前面已经做了几篇介绍,在 IdentityServer4 中定义了 Jwt Token 与 Reference Token 两种验证方式(https://www ...

  10. 常用的.NET开源项目(转)

    Json.NET http://json.codeplex.com/ Json.Net是一个读写Json效率比较高的.Net框架.Json.Net 使得在.Net环境下使用Json更加简单.通过Lin ...