我在我正在构建的针对 iOS、Android 和 Windows 手机的移动应用程序上使用 Lawnchair.js。我的问题是我有一个相对简单的函数(见下文),它从对象中读取数据并将其保存在 indexeddb 数据库中。大约有 4MB 的数据,当我在 Internet Explorer 中检查时(通过 Internet 选项),第一次运行时,我可以看到数据库大约是 7MB。如果我重新加载页面并使用相同的数据重新运行相同的功能,它会增加到 14MB,然后是 20MB。我使用相同的键,所以我的理解是这应该只是更新记录,但几乎就像每次都插入所有新记录一样。我在使用 websql 适配器的移动 safari 上使用 Lawnchair 也有类似的行为。有没有人见过这个或有任何关于为什么会这样的建议?
以下代码来自我用来填充数据库的函数。
populateDatabase: function(database,callback) {
'use strict';
var key;
try {
for(key in MasterData){
if(MasterData.hasOwnProperty(key)){
var itemInfo = DataConfig.checkForDataUpdates[DataConfig.keyMap[key]];
database.save({key:itemInfo["name"],hash:itemInfo["version"],url:itemInfo["url"],data:MasterData[key]});
}
}
callback(true);
} catch(e){
callback(false);
}
}
MasterData 是大型数据文件,itemInfo 包含键名、稍后用于检查 api 更新的哈希值以及更新位置的相对 url。创建数据库后,我将其传递给此函数,然后如果插入成功则返回 true,否则返回 false。
如前所述,我在 iOS 中看到了类似的问题,调用 database.save() 分配了大量内存但没有释放它,如果它填充数据库然后尝试更新一些记录,最终会导致崩溃。从等式中删除 Lawnchair 可以防止它崩溃,但在保存数据时它仍然分配大量内存。不确定这对于移动设备上的持久存储是否正常,Lawnchair 中的错误,或者我是一个菜鸟并且做了一些非常错误的事情,但我可以使用一些指针以及为什么 indexeddb 在每次保存时都会变得越来越大(至少在 IE10 的初始测试期间)??
编辑:索引数据库适配器的源代码在这里: https ://github.com/brianleroux/lawnchair/blob/master/src/adapters/indexed-db.js
这是我正在使用的保存功能的代码:
save:function(obj, callback) {
var self = this;
if(!this.store) {
this.waiting.push(function() {
this.save(obj, callback);
});
return;
}
var objs = (this.isArray(obj) ? obj : [obj]).map(function(o){if(!o.key) { o.key = self.uuid()} return o})
var win = function (e) {
if (callback) { self.lambda(callback).call(self, self.isArray(obj) ? objs : objs[0] ) }
};
var trans = this.db.transaction(this.record, READ_WRITE);
var store = trans.objectStore(this.record);
for (var i = 0; i < objs.length; i++) {
var o = objs[i];
store.put(o, o.key);
}
store.transaction.oncomplete = win;
store.transaction.onabort = fail;
return this;
},
创建新实例时,Lawnchair 使用 indexed-db 适配器中的 init 函数,如下所示。
init:function(options, callback) {
this.idb = getIDB();
this.waiting = [];
this.useAutoIncrement = useAutoIncrement();
var request = this.idb.open(this.name, STORE_VERSION);
var self = this;
var cb = self.fn(self.name, callback);
if (cb && typeof cb != 'function') throw 'callback not valid';
var win = function() {
// manually clean up event handlers on request; this helps on chrome
request.onupgradeneeded = request.onsuccess = request.error = null;
if(cb) return cb.call(self, self);
};
var upgrade = function(from, to) {
// don't try to migrate dbs, just recreate
try {
self.db.deleteObjectStore('teststore'); // old adapter
} catch (e1) { /* ignore */ }
try {
self.db.deleteObjectStore(self.record);
} catch (e2) { /* ignore */ }
// ok, create object store.
var params = {};
if (self.useAutoIncrement) { params.autoIncrement = true; }
self.db.createObjectStore(self.record, params);
self.store = true;
};
request.onupgradeneeded = function(event) {
self.db = request.result;
self.transaction = request.transaction;
upgrade(event.oldVersion, event.newVersion);
// will end up in onsuccess callback
};
request.onsuccess = function(event) {
self.db = event.target.result;
if(self.db.version != (''+STORE_VERSION)) {
// DEPRECATED API: modern implementations will fire the
// upgradeneeded event instead.
var oldVersion = self.db.version;
var setVrequest = self.db.setVersion(''+STORE_VERSION);
// onsuccess is the only place we can create Object Stores
setVrequest.onsuccess = function(event) {
var transaction = setVrequest.result;
setVrequest.onsuccess = setVrequest.onerror = null;
// can't upgrade w/o versionchange transaction.
upgrade(oldVersion, STORE_VERSION);
transaction.oncomplete = function() {
for (var i = 0; i < self.waiting.length; i++) {
self.waiting[i].call(self);
}
self.waiting = [];
win();
};
};
setVrequest.onerror = function(e) {
setVrequest.onsuccess = setVrequest.onerror = null;
console.error("Failed to create objectstore " + e);
fail(e);
};
} else {
self.store = true;
for (var i = 0; i < self.waiting.length; i++) {
self.waiting[i].call(self);
}
self.waiting = [];
win();
}
}
request.onerror = function(ev) {
if (request.errorCode === getIDBDatabaseException().VERSION_ERR) {
// xxx blow it away
self.idb.deleteDatabase(self.name);
// try it again.
return self.init(options, callback);
}
console.error('Failed to open database');
};
},