虚拟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. 2019-4-25 html学习笔记

    一.概念 文本   用于储存和记录文字信息的载体 html  超文本标记语言(本质就是给文本增加语义 如<h1></h1>就是给文字添加一级标题的语义) 注:互联网三大基石 有 ...

  2. VMware Tools安装教程

    安装依赖: sudo yum install eject 步骤: 确保 Linux 虚拟机已打开电源. 如果正在运行 GUI 界面,请打开命令 shell. 注意:以 root 用户身份登录,或使用 ...

  3. 分布式任务调度系统xxl-job搭建

    为解决分布式环境下定时任务的可靠性,稳定性,只执行一次的特性,我找到了个大众点评开源的分布式调度任务解决完整系统,下面我将一步步深入解读该系统,从基本的使用到源码的探究 下载 https://gith ...

  4. Kubernetes-2--安装部署

    kubernetes的部署方式: 1,kubeadm 2, 二进制安装包部署 条件准备: 1,关闭系统的swap分区 为什么要关闭swap分区,我还没有去看官方的说明,搜索到的答案是 kubernet ...

  5. Spring Boot读取配置的 5 种方式

    读取application文件 在application.yml或者properties文件中添加: info.address=USA info.company=Spring info.degree= ...

  6. Java 中的伪共享详解及解决方案

    1. 什么是伪共享 CPU 缓存系统中是以缓存行(cache line)为单位存储的.目前主流的 CPU Cache 的 Cache Line 大小都是 64 Bytes.在多线程情况下,如果需要修改 ...

  7. Liferay7 BPM门户开发之15: Liferay开发体系简介

    Liferay SDK 开发体系 主要分6种: Portlet Hook Theme Layout Templates Web Modules Ext Portlet :类似于servlet的web组 ...

  8. Python模块练习题

    练习题: 1.logging模块有几个日志级别? #INFO,WARNING,DEBUG,CRITICAL,ERROR 2.请配置logging模块,使其在屏幕和文件里同时打印以下格式的日志 2017 ...

  9. 2-3 用组件改写Todolist案例

     编写组件来改写2-2的Todolist案例

  10. 关于c++11中的thread库

    c++11中新支持了thread这个库,常见的创建线程.join.detach都能支持. join是在main函数中等待线程执行完才继续执行main函数,detach则是把该线程分离出来,不管这个线程 ...