离线JavaScript代理

在我的项目中,我需要一些功能,这些功能将使我不会在互联网连接中断的情况下丢失输入的数据,并且我想到了一个非常简单的“代理”程序,该代理程序使我不会在连接丢失时丢失数据,而在再次恢复连接时发送数据。 也许“经纪人”对他来说不是一个好名字,但不要严格判断。 我想分享,也许有人会有用。

关于项目


我的项目旨在考虑费用和收入,或作为房屋会计的简单版本。 它是作为一个渐进式Web应用程序创建的,因此可以方便地在移动设备上使用它,并可以打开Push通知功能,访问相机以读取条形码等。 有一个类似的移动应用程序称为ZenMoney,但我想要自己的东西和自己的方式。

想法的出现


我试图保持清晰的支出和收入记录,但是由于经常忘记添加必要的职位,尤其是关于现金,所以我不得不在发生“交易”时立即执行此操作。 有时我会在诸如地铁之类的公共交通工具中输入数据,尽管Wi-Fi网络广泛存在,但连接仍然经常发生。 可惜一切都冻结了,什么也没有发生,然后数据就丢失了。

这个想法来自使用诸如RabbitMQ之类的队列代理。 当然,我有一个更简单但不实用的解决方案,但是有一些与其原理相似的东西。 我认为您可以将所有内容(例如,在Cache或LocalStorage中)保存为带有“未满足”请求队列的对象,并且当出现连接时,可以安全地执行它们。 当然,它们不是按优先级执行的,这要归功于JS语言中对请求的异步处理,因为只有一个“订户”,所以优先级更高。 我遇到了一些困难,也许即使执行所有这些操作也似乎有些曲折,但这是一个可行的解决方案。 当然,它可以改进,但是现在我将描述现有的“原始”但可行的选择。

开始使用


我想到的第一件事是在没有连接的情况下将数据存储在哪里? PWA对我施加的服务要求与缓存很好地一起使用,但是使用缓存是否明智? 一个棘手的问题,我不会讨论。 通常,我认为LocalStorage对我来说更好。 由于LocalStorage存储key:value类型的值,因此必须将对象作为Json字符串添加。 在我的外部开发项目中,我向类文件夹添加了一个名为QueueBroker的目录。

档案结构
/**----**/
├── app.js
├── bootstrap.js
├── classes
│ └── QueueBroker
│ ├── index.js
│ └── Library
│ ├── Broker.js
│ └── Storage.js
├── components
/**----**/


我的项目是在Laravel + VueJs堆栈中完成的,因此需要一定的文件结构依赖性。 我不知道在这种情况下为类调用您自己的目录是正确的,所以我这样做了。

创建索引文件只是为了从嵌套库中插入模块。 这可能不是一个非常优雅的解决方案,但是我想这样做,以便如果我突然改变使用LocalStorage的主意,我将使用相同的方法编写另一个用于Storage的类,将其传递给代理构造函数,然后使用其他存储而无需进行任何更改。

index.js
 const Broker = require('./Library/Broker'); const Storage = require('./Library/Storage'); module.exports.Broker = Broker; module.exports.Storage = Storage; 


这种方法允许您仅连接脚本中需要的那些库,例如,如果我需要两者:

 import {Storage, Broker} from '../../classes/QueueBroker/index'; 

为了方便我更改存储类,我为Broker类创建了一种构造函数,可以将Storage对象作为参数传递给该构造函数,主要是它具有必要的功能。 我知道在ES6上我可以编写类和构造函数,但是决定采用老式的方法-原型。 我将直接通过代码写评论:

Broker.js
 const axios = require('axios'); //  axios /*     .    ,            front-end  */ function Broker(storage, prefix='storageKey') { this.storage = storage; this.prefix = prefix; /*     ,      .   storage   add     json */ if(this.storage.get('broker') === null) { this.broker = {}; this.storage.add('broker', this.broker) } else { //  , Storage    Json            this.broker = this.storage.getObject('broker'); } }; // ,           Broker.prototype.queueCount = function () { return Object.keys(this.broker).length; }; //  ""    Storage,    Broker.prototype.saveToStorage = function (method, url, data) { let key = this.prefix + '_' + (Object.keys(this.broker).length + 1); this.broker[key] = {method, url, data}; //            broker,        this.storage.add('broker', this.broker); }; // ,    ,    Broker.prototype.run = function () { for (let key in this.broker) { this.sendToServer(this.broker[key], key) } } /*    .        ,     method, url  data,        ,    ,    */ Broker.prototype.sendToServer = function (object, brokerKey) { axios({ method: object.method, url: object.url, data: object.data, }) .then(response => { if(response.data.status == 200) { //   ,    delete this.broker[brokerKey]; //  this.storage.add('broker', this.broker); } else { //   ;-) console.log(response.data) } }) .catch(error => { /*           ,       */ }); }; //   export module.exports = Broker; 


接下来,您需要存储对象本身,该对象将成功保存并从存储中检索所有内容

Storage.js
 //  debug-   function Storage(debug) { if(debug === true) { this.debugMode = true; } this.storage = window.localStorage; }; // ,     Json      Storage.prototype.addObjectToStorage = function (key, object) { this.storage.setItem(key, JSON.stringify(object)); }; //    (,   ) Storage.prototype.addStringToStorage = function (key, value) { this.storage.setItem(key, value); }; //    Storage.prototype.get = function (key) { return this.storage.getItem(key); }; //    Json ,       Storage.prototype.getObject = function (key) { try { return JSON.parse(this.storage.getItem(key)); } catch (e) { this._debug(e); this._debug(key + ' = ' + this.storage.getItem(key)); return false; } }; /* ,     ,  ,        ,   Json      */ Storage.prototype.add = function (key, value) { try { if(typeof value === 'object') { this.addObjectToStorage(key, value); } else if (typeof value === 'string' || typeof value === 'number') { this.addStringToStorage(key, value); } else { //    this._debug('2 parameter does not belong to a known type') } return this.storage; } catch (e) { //    ,    ,    if (e === QUOTA_EXCEEDED_ERR) { this._debug('LocalStorage is exceeded the free space limit') } else { this._debug(e) } } }; //  Storage.prototype.clear = function () { try { this.storage.clear(); return true; } catch (e) { this._debug(e) return false; } }; //    Storage.prototype.delete = function(key) { try { this.storage.removeItem(key); return true; } catch (e) { this._debug(e) return false; } }; // ,      Storage.prototype._debug = function(error) { if(this.debugMode) { console.error(error); } return null; }; //   module.exports = Storage; 


当以上所有条件都准备就绪时,您可以自行决定使用它,我可以这样使用:

在保存时使用
 //   Vue (methods) /*----*/ //      Storage   sendBroker(method, url, data) { let storage = new Storage(true); let broker = new Broker(storage, 'fundsControl'); broker.saveToStorage(method, url, data); }, //     fundsSave() { let url = '/pa/funds'; let method = ''; if(this.fundsFormType === 'create') { method = 'post'; } else if(this.fundsFormType === 'update') { method = 'put'; } else if(this.fundsFormType === 'delete') { method = 'delete'; } this.$store.commit('setPreloader', true); axios({ method: method, url: url, data: this.fundsFormData, }) .then(response=> { if(response.data.status == 200) { this.fundsFormShow = false; this.getFunds(); this.$store.commit('setPreloader', false); } else { this.$store.commit('AlertError', '    '); } }) //        .catch(error => { this.$store.commit('setAlert', { type: 'warning', status: true, message: '   . ,        ,   ' } ); this.fundsFormShow = false; this.$store.commit('setPreloader', false); //   ""  this.sendBroker(method, url, this.fundsFormData); console.error(error); }); }, 


重新连接时使用
 //   Vue /*--*/ methods: { /*----*/ /*   ,    ,   ,      ,      */ brokerSendRun() { let storage = new Storage(true); let broker = new Broker(storage, 'fundsControl'); //,   -   if(broker.queueCount() > 0) { // ,    broker.run(); //   ,   this.$store.commit('setAlert', {type: 'info', status: true, message: '      -  , ,      '}); } } } /*---*/ /*     , ,   ,          ,     ,        */ mounted() { this.brokerSendRun(); } /*---*/ 


聚苯乙烯


我很难谈论代码,因此我尝试在示例中尽可能详细地提供代码,并附上详细的注释。 如果您有改进此解决方案或改进本文的想法,我将很高兴在评论中看到它们。 我从自己在Vue上的项目中获取了一些示例,对此进行了解释,以弄清楚为什么要调用我的方法以及为什么要通过this访问它们。 我不会在本文中专门针对Vue进行此操作,因此,我没有提供其他组件代码,仅供参考。

Source: https://habr.com/ru/post/zh-CN428980/


All Articles