Dojo学习笔记——创建Dojo存储


难度:中级 Dojo版本:1.8

开始
新的 dojo/store系统想要代替旧的 dojo.data系统,部分是基于新的W3C对象存储API,目的是使得创建数据存储组件尽可能简单。创建符合 dojo/store的存储相当简单,大多数方法是可选的,只要需要其功能时才需实现。
基本的Dojo存储只实现部分 IndexedDB API,基本上这些方法涉及到存储内外的数据获取。 IndexedDB API的某些方面(例如索引和游标)没有实现,主要是因为它们在纯JavaScript环境中是不必要的。

创建第一个存储
Dojo中最简单的存储为 Memory store,用于处理大多数原始的数据管理任务。这是 dojo/store/Memory的基本API:
define(["../_base/declare", "dojo/store/util/SimpleQueryEngine"],
function(declare, SimpleQueryEngine) {
declare("dojo.store.Memory", null, {
constructor: function(options){ },
data: null,
index: null,
queryEngine: SimpleQueryEngine,
// what follows is the actual API signature
idProperty: "id",
get: function(id){ },
getIdentity: function(object){ },
put: function(object, options){ },
add: function(object, options){ },
remove: function(id){ },
query: function(query, options){ },
setData: function(data){ }
});
});
可见 dojo/store/Memory的签名相当简单,它涉及获取任何种类的数据( getquery),确保解决一致性问题( idPropertygetIdentity),允许创建新的条目,删除条目和更新条目(分别是 addremoveput)。另外,提供了设置初始数据集的方式( setData)。

内部数据结构
存储没有指定一组对象的实际数据结构,这是故意的,因为数据的结构通常依赖于数据的应用,存储没有事务来指定结构。
但是有一点应当总是在自定义存储中实现,即每一个数据对象唯一的标识符。存储的 idProperty性质指明哪个项目属性作为唯一标识符。默认为 id,但是可以是任何属性。
可以写一个存储来处理数据而不使用 idProperty,但强烈建议不要这样。最终没有某种唯一标识符的存储,每次都要依赖于在数据结构的所有元素中查找,从性能的角度看这将非常昂贵。

query:存储中最重要的方法
目前任何存储中最重要的方法是 query方法。这是主要的从存储获取信息的方式,而不改变任何内部数据结构。该方法必须接受两个参数, query对象和可选的 options对象。
query对象包含查询的标准和依赖于底层的数据引擎。 dojo/store带有一个内建的查询引擎 dojo/store/util/SimpleQueryEngine,该引擎处理大多数基本的查询需求,也可作为一个模板来写更为复杂的查询引擎。

创建查询引擎
dojo/store/util/SimpleQueryEngine演示了创建查询引擎的基本方法,想法是创建和返回一个函数,在一个对象数组上做一些类型的过滤(需要的话和其它事情),使用初始的标准集合传递给查询引擎。
创建查询引擎,要遵循 SimpleQueryEngine的结构,在一个闭包捕获查询参数,返回一个把一个元素数组作为唯一的参数的函数。基本的例子如下:
require(["dojo/_base/array"],
function(arrayUtil){
var myEngine = function(query, options){
var filteringFunction = function(object){
// do something here based on the passed query object
};
var execute = function(array){
var results = arrayUtil.filter(array, filteringFunction);
// do anything else needed, like sorting and pagination
return results;
}
execute.matches = filteringFunction;
return execute;
}
可以总是基于SimpleQueryEngine写自定义查询引擎,来处理任何所要的明确需求。也可以直接在存储的query方法创建一些什么,如果想要的话,例如让查询方法与远程服务器通信和让服务器返回结果。
确保有一个查询引擎可以操作数组的数据只是在自定义存储中创建 query方法的第一步,第二步是保证 query方法包装 dojo/store/util/QueryResults的返回值。

QueryResults是什么
dojo/store/util/QueryResults是一个简单的包装函数,用于查询的结果。其确保标准迭代方法存在于结果集,包括 forEachmapfilter
传递给 QueryResultsresults对象可以是数组或是 promise,即可以传递一个 promise对象给 QueryResults函数,相同的迭代函数依然可以用。
下面在Dojo的两个存储中看一下 query方法, Memory存储和 JsonRest存储。首先是 Memory存储:
define(["dojo/store/util/QueryResults"],
function(QueryResults){
....
// from dojo/store/Memory
query: function(query, options){
return QueryResults(
(this.queryEngine(query, options))(this.data)
);
}
Memory存储的内部数据结构是一个对象数组。通过调用 QueryResults,所有重要的迭代方法直接加入到结果对象,这意味着最好直接调用迭代方法:
var results = myMemoryStore.query({ foo: "bar" });
results.forEach(function(item){
// do something with the item
});
下面是 JsonRest存储的 query方法:
// from dojo/store/JsonRest
query: function(query, options){
var headers = {Accept: "application/javascript, application/json"};
options = options || {};
if(options.start >= 0 || options.count >= 0){
headers.Range = "items=" + (options.start || '0') + '-' +
(("count" in options && options.count != Infinity) ?
(options.count + (options.start || 0) - 1) : '');
}
// lang is from dojo/_base/lang
if(lang.isObject(query)){
// ioQuery from dojo/io-query
query = ioQuery.objectToQuery(query);
query = query ? "?" + query: "";
}
if(options && options.sort){
query += (query ? "&" : "?") + "sort(";
for(var i = 0; i < options.sort.length; i++) {
var sort = options.sort[i];
query += (i > 0 ? "," : "")
+ (sort.descending ? '-' : '+')
+ encodeURIComponent(sort.attribute);
}
query += ")";
}
// request from dojo/request
var results = request.get(this.target + (query || ""), {
handleAs: "json",
headers: headers
});
results.total = results.then(function(){
var range = results.response.getHeaders("Content-Range");
return range && (range=range.match(/\/(.*)/)) && +range[1];
});
return QueryResults(results);
}
这里 JsonRest存储没有使用查询引擎,而是使用 dojo/request调用了 REST服务,其本身返回一个 promiseQueryResults函数确保在返回的 promise上常见的迭代方法是可用的,以及这些方法看似以恰当的方式表现。
在内部QueryResults使用了 dojo.when 。要注意的是当写自定义存储时,应当总是确保query函数返回一个dojo/store/util/QueryResults包装的对象。

创建存储
为简单起见,该存储最终将看上去就像是 Memory存储,因为将只是保存一个内部数组的数据以及操作它。
define(["dojo/store/util/QueryResults", "dojo/_base/declare", "dojo/_base/lang", "dojo/request", "dojo/store/util/SimpleQueryEngine"],
function(QueryResults, declare, lang, request, SimpleQueryEngine){
// Declare the initial store
return declare(null, {
data: [],
index: {},
idProperty: "id",
queryEngine: SimpleQueryEngine,
constructor: function(options){
lang.mixin(this, options || {});
this.setData(this.data || []);
},
query: function(query, options){
return QueryResults(
(this.queryEngine(query, options))(this.data)
);
},
setData: function(data){
this.data = data;
// index our data
this.index = {};
for(var i = 0, l = data.length; i < l; i++){
var object = data[i];
this.index[object[this.idProperty]] = object;
}
}
});
});
注意构造函数的lang.mixin声明,这是一个常见的做法,允许通过构造函数的options参数对实例属性进行规范。在这里,最常用来为dataidProperty设置值。

添加getters
示例存储已实现了最重要的方法:一种设置存储数据的方式,一种查询基于 SimpleQueryEngine的数据的方式。也建立了索引机制,以便可以通过其身份快速返回一个条目,若是想要的话。
// in our declare from above
get: function(id){
return this.index[id];
},
getIdentity: function(object){
return object[this.idProperty];
}
这两个方法允许直接的数据访问,而不必经过查询,允许用户对一个给定的对象获得适当的唯一身份。若是存储的目的是(基本上)只读的,这都是需要在存储中定义的。

添加写入功能
大多数存储不是只读的。通常用户会想修改现有的对象,从自定义存储中添加和删除对象。为此将加入三个新方法: putaddremove
// in our declare from above
put: function(object, options){
var id = options && options.id
|| object[this.idProperty];
this.index[id] = object;
var data = this.data,
idProperty = this.idProperty;
for(var i = 0, l = data.length; i < l; i++){
if(data[i][idProperty] == id){
data[i] = object;
return id;
}
}
this.data.push(object);
return id;
},
add: function(object, options){
var id = options && options.id
|| object[this.idProperty];
if(this.index[id]){
throw new Error("Object already exists");
}
return this.put(object, options);
},
remove: function(id){
delete this.index[id];
for(var i = 0, l = this.data.length; i < l; i++){
if(this.data[i][this.idProperty] == id){
this.data.splice(i, 1);
return;
}
}
}
每次使用 put方法修改一个对象,每次创建一个新对象并加入到存储中使用 add方法,每次删除存储的一个对象使用 remove方法。 put方法是主要的,当改变一个对象时可以使用它,以便做类似更新的操作时,存储可以管理需要管理的事情。这里 putadd的实现唯一不同的是 add方法确保对象在存储中先前不存在。
最后的实现
这是最后的存储:
define(["dojo/store/util/QueryResults", "dojo/_base/declare", "dojo/store/util/SimpleQueryEngine"],
function(QueryResults, declare, SimpleQueryEngine){
// Declare the initial store
return declare(null, {
data: [],
index: {},
idProperty: "id",
queryEngine: SimpleQueryEngine,
constructor: function(options){
lang.mixin(this, options || {});
this.setData(this.data || []);
},
get: function(id){
return this.index[id];
},
getIdentity: function(object){
return object[this.idProperty];
},
put: function(object, options){
var id = options && options.id
|| object[this.idProperty];
this.index[id] = object;
var data = this.data,
idProperty = this.idProperty;
for(var i = 0, l = data.length; i < l; i++){
if(data[i][idProperty] == id){
data[i] = object;
return id;
}
}
this.data.push(object);
return id;
},
add: function(object, options){
var id = options && options.id
|| object[this.idProperty];
if(this.index[id]){
throw new Error("Object already exists");
}
return this.put(object, options);
},
remove: function(id){
delete this.index[id];
for(var i = 0, l = this.data.length; i < l; i++){
if(this.data[i][this.idProperty] == id){
this.data.splice(i, 1);
return;
}
}
},
query: function(query, options){
return QueryResults(
(this.queryEngine(query, options))(this.data)
);
},
setData: function(data){
this.data = data;
// index our data
this.index = {};
for(var i = 0, l = data.length; i < l; i++){
var object = data[i];
this.index[object[this.idProperty]] = object;
}
}
});
});
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值