首页 技术 正文
技术 2022年11月15日
0 收藏 418 点赞 4,818 浏览 6747 个字

背景

我们都知道频繁的dom给我们带来的代价是昂贵的,例如我们有时候需要去更新Table 的部分数据,必须去重新重绘表格,这代价实在是太大了,相比于频繁的手动去操作dom而带来性能问题,vdom很好的将dom做了一层映射关系,进而将在我们本需要直接进行dom的一系列操作,映射到了操作vdom.

  

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>virtualDom</title>
</head>
<body>
<div id="container"></div>
<button id="btn-change">修改</button>
<script type="application/javascript" src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script type="application/javascript">
const dataSource = [{
key: '1',
name: '胡彦斌',
age: 32,
address: '西湖区湖底公园1号'
}, {
key: '2',
name: '胡彦祖',
age: 42,
address: '西湖区湖底公园1号'
}]; const columns = [{
title: '姓名',
dataIndex: 'name',
key: 'name',
}, {
title: '年龄',
dataIndex: 'age',
key: 'age',
}, {
title: '住址',
dataIndex: 'address',
key: 'address',
}];
function render(data) {
var container = $('#container');
container.html(''); //清空容器
//添加表头
var $table =$('<table>')
$table.append($('<tr>'))
columns.map(function(item,index){
$table.append($('<td>'+item.title+'</td>'))
})
$table.append($('</tr>'))
//添加表体
dataSource.forEach(function(item){
$table.append($('<tr></tr><td>'+item.name+'</td>'+'<td>'+item.age+'</td>'+'<td>'+item.address+'</td></tr>'))
})
//只渲染一遍dom,尽然如此,还是需要清空容器
container.append($table)
}
$('#btn-change').click(function(){
dataSource[0].name="胡军网";
dataSource[1].address='南山区沙河东路1号'
//re——render
render(dataSource)
})
render()
</script>
</body>
</html>

解决

  1. virtual dom,虚拟 DOM
  2. 用 JS 模拟 DOM

什么是vdom

HTML DOM 结构:

<ul id="ul-list">
<li class="item">Item 1</li>
<li class="item">Item 2</li>
<li class="item">Item 3</li>
</ul>

针对于上面HTML DOM 结构,可以用JS表示为:

var ulE = {
tagName: 'ul', // 标签名
props: { // 属性用对象存储键值对
id: 'ul-list'
},
children: [ // 子节点
{tagName: 'li', props: {className: 'item'}, children: ["Item 1"]},
{tagName: 'li', props: {className: 'item'}, children: ["Item 2"]},
{tagName: 'li', props: {className: 'item'}, children: ["Item 3"]},
]
}

JS对象中抽取公共的部分属性,进一步封装:

export default Ele = (tagName, props, children) => {
this.tagName = tagName
this.props = props
this.children = children
}  

  

import * as el from 'ele';
var ol = el('ul', {id: 'ul-list'}, [
el('li', {className: 'item'}, ['Item 1']),
el('li', {className: 'item'}, ['Item 2']),
el('li', {className: 'item'}, ['Item 3'])
]);

通过snabbdom进行virtual dom(核心API:h函数、patch函数)

案例一: 对比局部更新添加修改ul中的li

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>snabbdom</title>
</head>
<body>
<div id="container"></div>
<button id="btn-change">修改</button>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-class.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-props.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-style.min.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.min.js"></script>
<script type="application/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');
//定义 virtual node
var vnode = h('ul#ul-list',{},[
h('li.item',{},'item1'),
h('li.item',{},'item2')
])
patch(container,vnode);
document.getElementById('btn-change').addEventListener('click',function () {
var newVnode = h('ul#ul-list',{},[
h('li.item',{},'item1'),
h('li.item',{},'西湖区湖底公园1号'),
h('li.item',{},'西湖区湖底公园2号'),
h('li.item',{},'西湖区湖底公园3号')
])
patch(vnode,newVnode);
})
</script>
</body>
</html>

  item1 所在的li不会进行dom渲染,只有新增或者修改的node才会发生改变,执行结果如下所示:

浅谈 Virtual DOM 的那些事

案例二: 局部更新部分Table 数据(使用Vitual DOM 性能的提升)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>snabbdom</title>
</head>
<body>
<div id="container"></div>
<button id="btn-change">修改</button>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-class.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-props.min.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-style.min.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.min.js"></script>
<script type="application/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');
const dataSource = [{
key: '1',
name: '胡彦斌',
age: 32,
address: '西湖区湖底公园1号'
}, {
key: '2',
name: '胡彦祖',
age: 42,
address: '西湖区湖底公园1号'
}]; const columns = [{
title: '姓名',
dataIndex: 'name',
key: 'name',
}, {
title: '年龄',
dataIndex: 'age',
key: 'age',
}, {
title: '住址',
dataIndex: 'address',
key: 'address',
}];
var vdom=null;
function render(dataSource) {
var titleTr= [];
titleTr.push(h('td',{},' '))
columns.forEach(function(item){
if(item.hasOwnProperty('title')){
titleTr.push(h('td',{},item['title']))
}
})
var vTitle = h('tr',{},titleTr);
var vBody =dataSource.map(function(item){
const vp= []
for(var i in item) {
if(item.hasOwnProperty(i)){
vp.push(h('td',{},item[i]))
}
}
return h('tr',{},vp)
})
vBody.unshift(vTitle);
var vTable = Object.assign([],vBody);
var newVnode = h('table',{},vTable)
if(!vdom){
vdom = newVnode;
patch(container,vdom);
}else{
patch(vdom,newVnode);
}
}
document.getElementById('btn-change').addEventListener('click',function () {
dataSource[0].name="胡军网";
dataSource[1].address='南山区沙河东路1号'
//re——render
render(dataSource)
})
render(dataSource)
</script>
</body>
</html>

  执行结果如下所示:

浅谈 Virtual DOM 的那些事

patch函数——patch(container,vDom)过程的简单实现

/**
*
* @param container 容器
* @param vDom 虚拟dom
* @constructor
*/var ulE = {
tagName: 'ul', // 标签名
props: { // 属性用对象存储键值对
id: 'ul-list'
},
children: [ // 子节点
{tagName: 'li', props: {className: 'item'}, children: ["Item 1"]},
{tagName: 'li', props: {className: 'item'}, children: ["Item 2"]},
{tagName: 'li', props: {className: 'item'}, children: ["Item 3"]},
]
}export default function VDomCreateElement(vDom){
var tagName=vDom.tagName || '';
var props =vDom.props || {};
var children =vDom.children || [];
var tagNameEle =document.createElement(tagName);
for(var prop in props){
if(props.hasOwnProperty(prop)){
tagNameEle.setAttribute(prop,props[prop])
}
}
if(!children){
return tagNameEle;
}else{
children.forEach(function(item){
tagNameEle.appendChild(VDomCreateElement(item)) //不断递归生成child Node
})
}
return tagNameEle;
}

patch函数——patch(vDom,newVDom)过程的简单模拟实现

  

/**
* vDOM 简单diff 对比 新的dom渲染到旧的dom
* @param vDom 老vDom
* @param newVDom 新vDom
*/
export function vDomDiff(vDom,newVDom){
var vDomChilden = vDom.children || [];
var newVDomChilden = newVDom.children || [];
//假设 tagName 相同
vDomChilden.forEach(function(item,index){
if(!newVDomChilden[index]){
return;
}
if(item.tagName === newVDomChilden[index].tagName){
//两者tagName 一样 递归
VDomCreateElement(item,newVDomChilden[index]);
}else {
//两者tagName 不一样 替换
replaceNode(item,newVDomChilden[index])
}
})
}/**
* dom操作 替换
* @param vDom
* @param newVDom
*/
function replaceNode(vDom,newVDom){
//dom操作 node替换
// ....}

  

Visual DOM 为何使用diff算法

Visual DOM找出DOM 中不同,进而更新DOM,diff算法同样也是找出文件中的不同进行对比,diff应用在linux,git……,

源码地址

https://github.com/10086XIAOZHANG/VirtualDOMDemo  

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,028
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,518
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,365
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,146
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,780
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,857