TWaver HTML5 + Node.js + express + socket.io + redis(四)

2024-04-28 14:38

本文主要是介绍TWaver HTML5 + Node.js + express + socket.io + redis(四),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文出处:http://twaver.servasoft.com/?p=3764

 

 

接上一回TWaver HTML5 + Node.js + express + socket.io + redis(三), 这一篇您将了解到

1. 如何保存更改后的拓扑数据 (包括新增的, 修改的, 删除的)
2. 如何广播更改后的拓扑数据 (仅仅广播更改的数据)

下面是mac和iphone上的效果图, mac或iphone上的修改都将及时互相同步:

一. 先来看后台如何实现

后台需要做两件事情: 保存修改以及广播修改; 其中修改又分为是新增, 修改, 还是删除. 保存修改很容易, 无非就是对数据库的增删改, 广播数据也很容易, 调用Socket.emit之前, 先设置广播标记就ok了: Socket.broadcast.emit. 而且这个广播只会通知其他客户端, 发送这个广播的客户端不会收到广播, 这正好是我们需要的, 所以后台代码就好写了:

添加保存数据的socket.io监听, 里面保存数据后, 广播之:

//保存数据
client.on('saveData', function (datas) {
if (!datas) {
return;
}
//保存新增网元
save(datas.add);
//保存修改网元
save(datas.change);
//删除网元
remove(datas.remove);
//广播更新
client.broadcast.emit('broadcast', datas);
});


保存数据的函数如下:

//保存网元
function save (datas) {
if(!datas){
return;
}
var elements = {};
for (var i=0,n=datas.length,data; i<n; i++) {
data = datas[i];
elements[data.id] = JSON.stringify(data);
}
redis.hmset('datas', elements);
};
//删除网元
function remove (datas) {
if(!datas){
return;
}
var ids = [];
for (var i=0,n=datas.length; i<n; i++) {
ids.push(datas[i].id);
}
redis.hdel('datas', ids);
};


 

二. 前台实现

分两步: 监听数据的增删改并自动保存, 响应广播更新
1. 监听数据的增删改并自动保存

TWaver HTML5的数据模型提供了各种监听器, 以便在数据更改后做响应处理. 其中:
DataBox.addDataPropertyChangeListener用于监听数据容器里数据的属性变化, 也即对数据的修改, 其回调函数的参数包含属性: property(发生变化的属性), oldValue(旧值), newValue(新值), source(发生变化的数据)

DataBox.addDataBoxChangeListener用于监听数据容器的变化, 也即数据的添加, 删除以及清空, 其回调函数的参数包含属性: kind(容器变化类型, 可选值为add(新增), remove(删除), clear(清空)), data(新增或删除的数据), datas(清空的数据集合)

这里实现保存数据的思路是:
i> 如果有网元被修改, 则将被修改的网元加上change标记, 其中判断isChanging标志很重要, 因为需要用此标志区分是用户更改还是响应广播更新(也即程序更新)

//添加网元属性更改监听器
box.addDataPropertyChangeListener(function (e) {
//如果正在响应广播更新,则返回
if (isChanging) {
return;
}
//如果属性为add, change 则返回
if (e.property === 'C:add' || e.property === 'C:change') {
return;
}
//设置保存数据标记
needSave = true;
//将有属性更改的节点置上change标记
e.source.setClient('change', true);
});

ii> 如果有新增网元, 则将新增网元加上add标记, 如果有网元被删除, 则存入map中

//添加数据容器更改监听器
box.addDataBoxChangeListener( function (e) {
//如果正在响应广播更新,则返回
if (isChanging) {
return;
}
//设置保存数据标记
needSave = true;
if (e.kind === 'add') {
//将新增的节点置上add标记
e.data.setClient('add', true);
} else if (e.kind === 'remove') {
//如果网元没有新增标记, 则存储到被删除网元列表中
if (!e.data.getClient('add')) {
//存储被删除网元
removedElements[e.data.getId()] = e.data;
}
}
});


iii> 启动定时器, 保存更改
定时器代码如下:

//自动保存
function setAutoSave (value) {
if(value){
//定时器,每隔1秒钟保存修改
timer = setInterval(function(){
save();
}, 1000);
}else{
window.clearTimeout(timer);
}
}


 

保存数据代码如下, 需要注意的是, 得到要添加和被修改的网元后, 需要将其add和change标记置为false, 以避免重复保存:

//保存数据
function save () {
//如果无数据添加、修改、删除,则直接返回
if(!needSave){
return;
}
var add = [];
var change = [];
var remove = [];
isChanging = true;
box.forEach(function(data){
//新增的网元
if(data.getClient('add')){
add.push(toData(data));
}
//修改的网元
else if(data.getClient('change')){
change.push(toData(data));
}
//清除网元新增和修改标记
data.setClient('add', false);
data.setClient('change', false);
});
isChanging = false;
//删除的网元
for(var id in removedElements){
remove.push(toData(removedElements[id]));
}
if(add.length == 0 && change.length == 0 && remove.length == 0){
return;
}
//初始化待删除数据
removedElements = {};
//还原保存数据标记
needSave = false;
//构造更新数据
var datas = {};
if(add.length != 0){
datas.add = add;
}
if(remove.length != 0){
datas.remove = remove;
}
if(change.length != 0){
datas.change = change;
}
socket.emit('saveData', datas);
}


从网元获取要持久化的数据代码如下:

//从网元获取持久化数据
function toData (element) {
if(element instanceof twaver.Node) {
return {
id: element.getId(),
name: element.getName(),
location: element.getLocation()
};
} else {
return {
id: element.getId(),
name: element.getName(),
from: element.getFromNode() ? element.getFromNode().getId() : null,
to: element.getToNode() ? element.getToNode().getId() : null
};
}
}


 

2. 响应广播更新

分为新增, 删除以及修改三种情况, 其中很重要的是在做这些事情之前先设置isChanging为true, 以防止重复广播更新

//响应广播更新
function onBroadcast (data) {
if (!data) {
return;
}
//设置响应广播更新标记,防止重复更新
isChanging = true;
var i, c, add=data.add, change=data.change, remove=data.remove;
//添加节点
if (add) {
for (i=0,c=add.length; i<c; i++) {
if (add[i].from) {
addLink(add[i]);
} else {
addNode(add[i]);
}
}
}
//修改节点
if (change) {
for (i=0,c=change.length; i<c; i++) {
changeData(change[i]);
}
}
//删除节点
if (remove) {
for (i=0,c=remove.length; i<c; i++) {
removeData(remove[i]);
}
}
//还原更新广播数据
isChanging = false;
}


增删改网元代码如下:

//添加节点
function addNode (data) {
//构造节点
var node = new twaver.Node(data);
//添加节点
box.add(node);
}
//添加节点
function addLink (data) {
//查找from节点
var from = box.getDataById(data.from);
//查找to节点
var to = box.getDataById(data.to);
//构造连线
var link = new twaver.Link({id: data.id, name: data.name}, from, to);
//添加连线
box.add(link);
}
//修改网元
function changeData (data) {
var element = box.getDataById(data.id);
//修改节点
if (element instanceof twaver.Node) {
element.setLocation(data.location.x, data.location.y);
element.setName(data.name);
}
//修改连线
else if (element instanceof twaver.Link) {
element.setName(data.name);
element.setFromNode(box.getDataById(data.from));
element.setToNode(box.getDataById(data.to));
}
//网元被删除
else {
//网元被删除, 无需修改
}
}
//删除网元
function removeData (data) {
box.removeById(data.id);
}


 

最后, 附上本文的完整demo:TWaverHTML5Demo

这篇关于TWaver HTML5 + Node.js + express + socket.io + redis(四)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/943513

相关文章

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Redis MCP 安装与配置指南

《RedisMCP安装与配置指南》本文将详细介绍如何安装和配置RedisMCP,包括快速启动、源码安装、Docker安装、以及相关的配置参数和环境变量设置,感兴趣的朋友一起看看吧... 目录一、Redis MCP 简介二、安www.chinasem.cn装 Redis MCP 服务2.1 快速启动(推荐)2.

从入门到精通详解LangChain加载HTML内容的全攻略

《从入门到精通详解LangChain加载HTML内容的全攻略》这篇文章主要为大家详细介绍了如何用LangChain优雅地处理HTML内容,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录引言:当大语言模型遇见html一、HTML加载器为什么需要专门的HTML加载器核心加载器对比表二

Redis中Stream详解及应用小结

《Redis中Stream详解及应用小结》RedisStreams是Redis5.0引入的新功能,提供了一种类似于传统消息队列的机制,但具有更高的灵活性和可扩展性,本文给大家介绍Redis中Strea... 目录1. Redis Stream 概述2. Redis Stream 的基本操作2.1. XADD

Knife4j+Axios+Redis前后端分离架构下的 API 管理与会话方案(最新推荐)

《Knife4j+Axios+Redis前后端分离架构下的API管理与会话方案(最新推荐)》本文主要介绍了Swagger与Knife4j的配置要点、前后端对接方法以及分布式Session实现原理,... 目录一、Swagger 与 Knife4j 的深度理解及配置要点Knife4j 配置关键要点1.Spri

Redis出现中文乱码的问题及解决

《Redis出现中文乱码的问题及解决》:本文主要介绍Redis出现中文乱码的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 问题的产生2China编程. 问题的解决redihttp://www.chinasem.cns数据进制问题的解决中文乱码问题解决总结

前端如何通过nginx访问本地端口

《前端如何通过nginx访问本地端口》:本文主要介绍前端如何通过nginx访问本地端口的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、nginx安装1、下载(1)下载地址(2)系统选择(3)版本选择2、安装部署(1)解压(2)配置文件修改(3)启动(4)

Redis的持久化之RDB和AOF机制详解

《Redis的持久化之RDB和AOF机制详解》:本文主要介绍Redis的持久化之RDB和AOF机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述RDB(Redis Database)核心原理触发方式手动触发自动触发AOF(Append-Only File)核

Redis分片集群、数据读写规则问题小结

《Redis分片集群、数据读写规则问题小结》本文介绍了Redis分片集群的原理,通过数据分片和哈希槽机制解决单机内存限制与写瓶颈问题,实现分布式存储和高并发处理,但存在通信开销大、维护复杂及对事务支持... 目录一、分片集群解android决的问题二、分片集群图解 分片集群特征如何解决的上述问题?(与哨兵模

HTML中meta标签的常见使用案例(示例详解)

《HTML中meta标签的常见使用案例(示例详解)》HTMLmeta标签用于提供文档元数据,涵盖字符编码、SEO优化、社交媒体集成、移动设备适配、浏览器控制及安全隐私设置,优化页面显示与搜索引擎索引... 目录html中meta标签的常见使用案例一、基础功能二、搜索引擎优化(seo)三、社交媒体集成四、移动