用NW.js构建跨平台桌面应用(4)-数据持久化

2020-06-15 14:59:07 浏览数 (1)

与web开发中主要将数据保存在服务器端不同(cookie中仅保存极少量信息且会过期),数据持久化(Data Persistence)是本地应用开发中的常见需求,通俗的讲就是将瞬时数据(比如内存中的数据,断电即失效)保存为持久数据(比如写入数据库中长久保存); 由于NW.js的特性,可供选择的本地持久化方案非常丰富,既可以依赖于Node.js直接使用NeDB、LowDB等第三方数据库方案,也可以使用HTML5及其相关标准中提供的WebSQL、WebStorage、IndexedDB等方案,甚至直接将简单的数据保存到本地文件中

[I]. 直接存储数据到本地文件

正如之前的文章提到过的,NW.js提供了App.dataPath来访问系统的应用数据目录, 可以利用其方便的存储应用配置文件等

不同平台上的具体指向:

  • Win: $LOCALAPPDATA%/
  • Linux: ~/.config/
  • Mac: ~/Library/Application Support/
代码语言:javascript复制
var fs = require('fs');
var path = require('path');function saveSettings (settings, callback) {
   var file = 'my-settings-file.json';
   var filePath = path.join(nw.App.dataPath, file);
   fs.writeFile(filePath, settings, function (err) {
       if (err) {
           console.warn(err.message);
           return;
       } else if (callback) {
           callback();
       }
   });
}var mySettings = {
   "language": "zh-CN",
   "theme": "dark"
};saveSettings(mySettings, function () {
   console.log('Settings saved');
});

[II]. Web storage

Web storage 是浏览器中最简单的一种数据存储技术,可以暂时或永久的保存 key-value 数据;适合于简单且非密集的数据事务场景

2.1 基本规则

  • 每个domain最多5MB的存储限制
  • 同步调用,有可能阻塞主渲染进程
  • 直接读写磁盘,可能影响性能
  • 没有高级的索引和查询等
  • 只能用字符串存取数据,复杂数据和图片应分别用JSON和base64转码后存储
  • 可以在 DevTools->Application->Storage->Local Storage 标签页中查看已存储的数据

2.2 操作方法

代码语言:javascript复制
//存数据
localStorage.setItem('myKey', myValue);//取数据, 无结果返回null
localStorage.getItem('myKey');
localStorage.myKey;
localStorage['myKey'];//删数据
localStorage.removeItem('myKey');
delete localStorage.myKey;
localStorage.clear();//存储数据时会触发事件(可用于监听其他页面是否修改了某个数据)
window.addEventListener('storage' e=>console.log(e.key, e.newValue));

2.3 基于localStorage的StoreDB

StoreDB(https://github.com/djyde/StoreDB) 是一个基于localStorage的本地储存库,通过模拟MongoDB的一些API和概念(如“集(collection)”和“文档(document)”),使你能使用 localStorage 储存复杂数据

代码语言:javascript复制
//插入数据
storedb('players').insert({
   "name":"Randy",
   "sex":"male",
   "score":20
},function(err,result){
 if(!err){
   //do sth...
 } else //do sth...
})//更新数据
storedb('players').update(
   {"name":"Randy"},
   {"$inc":{"score":"10"}
},function(err){
 if(!err){
   //do sth...
 } else //do sth...
})//查找数据
storedb('players').find({
   "name":"Randy"
},function(err,result){
 if(!err){
   //use result to do sth...
 } else //do sth...
})

[III]. Web SQL 数据库

  • Web SQL 是一个基于 SQLite 的数据库规范,使得在浏览器端使用 SQL 语言(如select, insert, update, delete, joins, inner selects等)变得简便易行 --- 这也是其最大的优点
  • Web SQL Database API 实际上未包含在 HTML 5 规范之中,它是一个独立的规范
  • 其 API 有同步的,也有异步的, 同步版本的 API 只在工作线程(Worker Threads)上有用,由于并不是所有的浏览器都支持工作线程,一般情况下,都会使用异步 API
  • 基本没有容量限制
  • 虽然 W3C 官方已经声明不再维护 Web SQL Database 规范,但由于其广泛的实现程度,了解这些 API 对 Web 开发还是非常有必要的

3.1 基本概念

  • SQL语句(SQL statement): 一条SQL查询语句
  • 事务(transaction): 管理并顺序执行若干条SQL语句的容器,可以嵌套
  • 错误回调:每条 SQL statement 及其所处的 transaction 都有各自的错误回调,用于精细或集约的捕获错误并引发回滚
  • 回滚(roll back):错误发生时撤销单条语句或整个事务的操作
  • 可以在 DevTools->Application->Storage->Web SQL 标签页中查看已存储的数据

3.2 常用方法

  • openDatabase(): 新建数据库对象或打开已有的
  • transaction(): 执行一个事务并在错误发生时回滚
  • executeSql(): 执行SQL语句
代码语言:javascript复制
//初始化一个数据库
var db = openDatabase(
   'myDB', //数据库名
   '1.0', //版本号
   'My Database', //显示名
   2 * 1024 * 1024, //容量
   db=> console.log() //成功回调
);//执行一个事务
db.transaction(t=>{
   //创建表
   let sql1 = "CREATE TABLE IF NOT EXISTS articles (id INTEGER PRIMARY KEY, title TEXT, content TEXT)";
   t.executeSql(sql1);
   //插入一条数据
   let data1 = ['标题1', '内容1'];
   let sql2 = "INSERT INTO articles(title, content) VALUES (?, ?)";
   t.executeSql(sql2, data1);
   
   //取数据
   db.transaction(t=>{
       t.executeSql(
           "SELECT * FROM articles",
           [],
           (transaction, resultSet)=> {
               let
                   data = []
                   ,row
                   ,rowNum = resultSet.rows.length
               ;
               for (let i = 0; i<rowNum; i  ) {
                   row = resultSet.rows.item(i);
                   data.push(row);
               }
               console.log(data);
           }
       );
   });
   
   //向一个假定并不存在的表里插入数据,引发错误
   t.executeSql(
       "INSERT INTO logs(message) VALUES (?)",
       ["我是一条日志"],
       function sqlStatementCallback(transaction, resultSet) {},
       function sqlStatementErrorCallback(transaction, err) {
           //处理err
           //该条语句被回滚,如果有后续语句继续执行
           return false; //如果返回true,则会传递到事务的errorCallback
                       //,并引发整个事务的回滚
       }
   );
}, sqlError=> {
   console.warn(sqlError.message);
});

[IV]. IndexedDB

  • 存储 key-value 数据的事务型 NoSQL 非关系型数据库系统
  • 可以存储复杂的javascript对象,并用一个或多个索引查询
  • 基本没有容量限制
  • 所有操作都是异步的

4.1 基本概念

  • 数据库: 由 indexedDB.open(name, ver) 方法打开,返回一个IDBDatabase接口格式的异步请求对象
  • ObjectStore: 相当于关系型数据库中的数据库表table,实现IDBObjectStore接口
  • 事务(transaction): 所有ObjectStore的读写操作都借由事务完成
  • 可以在 DevTools->Application->Storage->IndexedDB 标签页中查看已存储的数据

4.2 代码示例

代码语言:javascript复制
var
   dbName = 'DBName'
   ,dbVer = 1
   ,openDbRequest = indexedDB.deleteDatabase(dbName) && indexedDB.open(dbName, dbVer)
;
openDbRequest.onerror = e=>console.warn(e);
openDbRequest.onsuccess = e=>{
   let db = e.target.result;
   console.log('数据库建立成功:', db.version);
};
openDbRequest.onupgradeneeded = e=> {
   let db = e.target.result;
   console.log(e);   let
       storeName = 'personsStore'
       ,storeOptions = {
           keyPath: 'id',
           autoIncrement: true
       }
   ;   // if (db.objectStoreNames.contains(storeName)) {
   //  console.log('删除了旧数据库');
   //  db.deleteObjectStore(storeName);
   // }
   let store = db.createObjectStore(storeName, storeOptions);
   store.createIndex('by_name', 'name', {unique: false});
   store.createIndex('by_age', 'age', {unique: false});   setTimeout(function() {
       let
           ts = db.transaction(['personsStore'], 'readwrite')
           ,persons = ts.objectStore('personsStore')
       ;
       persons.add({
           name: '小红',
           age: 30
       });
       persons.add({
           name: '小明',
           age: 28
       }).onsuccess = function() {
           var key = this.result;
           alert(key);           persons.index('by_age').getAll().onsuccess =
               e=>console.log('index by_age', e.target.result);
           persons.get(key).onsuccess =
               e=>console.log('get 2', e.target.result);
       };
   }, 2000);
};

4.3 完整接口文档

https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API

4.4 第三方封装实现

  • Dexie: http://dexie.org/
代码语言:javascript复制
var db = new Dexie("friend_database");
db.version(1).stores({
  friends: 'name,shoeSize'
});
db.friends.put({name: "Nicolas", shoeSize: 8}).then (function(){
  return db.friends.get('Nicolas');
}).then(function (friend) {
  alert ("Nicolas has shoe size "   friend.shoeSize);
}).catch(function(error) {
 alert ("Ooops: "   error);
});
  • PouchDB: https://pouchdb.com/
代码语言:javascript复制
db.get('mittens').then(function (doc) {
  // okay, doc contains our document
}).catch(function (err) {
  // oh noes! we got an error
});

db.allDocs({include_docs: true}).then(res => {
    console.log(JSON.stringify(res))
});

db.get('mittens').then(function (doc) {
  return db.remove(doc);
});
  • https://github.com/nwjs/nw.js/wiki/Save-persistent-data-in-app
  • http://www.ccydesign.com/websql-amd-indexdb/
  • https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API
  • https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDBAPI/UsingIndexedDB
  • http://www.ibm.com/developerworks/library/wa-indexeddb/index.html
  • http://blog.csdn.net/cainiaokan/article/details/29366105
  • https://www.w3.org/TR/IndexedDB/#dfn-multientry
  • http://web.jobbole.com/81793/
  • http://www.csdn.net/article/2012-10-12/2810660-NoSQL-MongoDB-HBase
  • http://baike.baidu.com/item/NoSQL
  • http://www.cnblogs.com/xiaowei0705/archive/2011/04/19/2021372.html
  • http://www.cnblogs.com/dolphinX/p/3415761.html

* 原创文章转载请注明出处

0 人点赞