虚拟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. HTML5之FileReader文件读取接口

    FileReader用来把文件读入内存,并且读取文件中的数据.FileReader接口提供了一个异步API,使用该API可以在浏览器主线程中异步访问文件系统,读取文件中的数据. 1.FileReade ...

  2. Git入门基础详情教程

    前言 写了一篇文章<一篇文章了解Github和Git教程>还觉得不错,继续写了<为了Github默默付出,我想了解你>,那么继续写Git 基础知识. Git 官网:https: ...

  3. jQuery应用实例5:表单验证

    1.validation插件的使用: 入门案例: <html> <head> <meta charset="UTF-8"> <title& ...

  4. Spring Boot --- Swagger基本使用

    1. pom <!-- swagger2 --> <dependency> <groupId>io.springfox</groupId> <ar ...

  5. 【sping揭秘】14、@before @AfterThrowing

    @before 基础模式,我们先直接看一下就可以了,比较简单,网上一堆... 不是我装逼哈,我学了那么久spring,aop的皮毛也就是网上的那些blog内容,稍微高级点的我也不会,这里跳过基础部分 ...

  6. 学生成绩管理系统3.0(JSP+Servlet+MySQL)

    源代码:戳这里! (2019-01-08 更新 惊讶于这么久了还有人看这个项目 于是把代码传到 github 了(辣鸡CSDN) https://github.com/G-lory/StudentAc ...

  7. python 中range函数的用法

    一. range(start,end,step) 二.代码 [code1] for i in range(1,10,2): print("i=",i) [result1] i= 1 ...

  8. Jenkins配置AD域认证

    Jenkins配置AD域认证 #检测域控地址ping youad.com指向的IP #如果不是实际域控ip地址,则修改hosts vi /etc/hosts #192.168.100.100替换为实际 ...

  9. 4 spring 创建对象的三种方式

    方式1. 通过构造方法创建     1.1 无参构造创建:默认情况.     1.2 有参构造创建:需要明确配置         1.2.1 需要在类中提供有参构造方法         1.2.2 在 ...

  10. SpringBoot的幕后推手...

    一.背景 ​ 这两年随着微服务的盛行,SpringBoot框架水到渠成的得到了高曝光,作为程序猿的我们,现在要是不知道一点SpringBoot相关的东西,貌似出去找工作都会被深深地鄙视,不过在我们开始 ...