Welcome to the Swop 🎉
![stars](https://img.shields.io/github/stars/imtaotao/Swop.svg)
swop 是一个用于JavaScript
与客户端进行数据交互应用程序,他提供了简洁的api
来帮助开发者来做这些事情。
swop 的来源
在我们与客户端交互的时候,大量的数据散落在项目各个文件,或者集中于繁琐的redux
和window
对象之中,管理维护困难,操作麻烦,使得开发效率很低。
swop 要解决什么样的问题
swop 使用 aop
的理念进行设计开发,通过对于数据的拦截来做一些事情,swop 内置了中间件,参考一下express
和redux
的middleware
,但不同的是 swop 的middleware
没有next函数,swop 能让开发者更好的处理数据,更好的异步和错误处理。
API
options
钩子函数
swop.send
考虑到不同业务场景下的需求不同,swop 最终与客户端交互的时候必须通过 send 方法进行。
S.send = function (name, send_data, origin_data) {
...
}
swop.onerror
swop 提供一个捕捉错误的钩子函数,如果 onerror 函数被定义了, swop 发出的错误都会被捕捉到,否则,swop 会直接在控制台输出错误。
S.onerror = function (msg, stack, error_text) {
...
}
swop 类 api
绑定属性 api
实例化 swop
swop 可以通过两种方式来实例化。
import { Swop, CreateSwop } from 'swop-data';
const S = new Swop(options);
const S = CreateSwop(options);
S.create('dataOne', 1);
S.dataOne.set(2).get();
如果你是用typescript
进行的开发
import {
Swop,
CreateSwop,
ContainerDataTypes as C,
} from 'swop-data';
export type A = C<I, keyof D>;
export type I =
'interfaceOne' |
'interfaceTwo';
export type D = {
'dataOne': A;
'dataTwo': A;
'dataThree': A;
}
const S = new Swop<I, D>(options);
S.creata('dataOne');
S.types.dataOne.get();
interface _Swop extends Swop<I, D>, D {}
const S = CreateSwop<_Swop>(options);
S.dataOne.get();
S['dataOne'].get();
swop 类 api
call
call 方法是于客户端进行通信的入口函数,他需要传入一个name字符和需要发送的数据。
S.call(name, data).then(([value, args]) => {
...
args.next();
}).catch(err => {
...
})
async function call_height () {
const data = await S.call(name, data)
...
}
S.call(name).then(([value, args]) => {
args.next(`上一次的响应值为:${value}`);
})
response
response 方法是客户端的入口函数。返回一个promise。
window.callback = data => S.response(data);
window.callback = data => {
S.response(data).then(_ => {
console.log('success');
}).catch(err => {
...
})
};
create
create 方法会创建一个绑定属性和绑定数据,返回值为this
。
S.create(attr_name, default_value, read_only)
.create(attr_name);
use
use 方法是 swop 提供的一个中间件函数,你可以通过 use 方法来注入一些中间件,中间件的注入与先后顺序相关,use 方法返回的是this
,所以你可以像 jQuery 那样链式调用。
S.use(name, val => {
if (typeof val.value === 'string') {
val.value = JSON.parse(val.value);
}
})
S.use(name, [value, match] => {
...
})
get_all_data
get_all_data 能够获取所有的绑定数据。
const all_data = S.get_all_data();
get_queue
swop 把当前接口的所有客户端响应都放到一个队列里面,get_queue 方法会返回当前接口的客户端响应队列。
const queqe = S.get_queue(name);
get_funs
当调用 call 方法与客户端进行数据交互时,会生成一个接收客户端响应的集合,每个集合由一个fun_body
和id
组成。get_funs 能够得到当前接口的所有集合。
const funs = S.get_funs(name);
const ids = funs.map(val => val.id);
ids.forEach((id, i) => {
S.response(JSON.stringify({
id,
origin_data: {
xx: 'xxx',
}
}))
})
clear_polling
clear_polling 方法清除绑定属性的轮询。如果name
为空,则清空所有绑定属性的轮询,返回值为this
。
S.clear_polling(name);
S.clear_polling();
绑定属性 api
当通过create
api 创建一个静态属性后,每个静态属性都会生成对应的方法,需要注意的是当生成的绑定数据是readOnly
时,当前绑定属性只有get
方法。
- 假定以下 api 描述的绑定属性名为 dataOne。
get
const data = S.dataOne.get();
set
set 方法会给当前绑定数据重新赋值,返回值是当前绑定属性
S.dataOne.set(1);
S.dataOne.set(1).get();
S.use('dataOne', val => val.value *= 100);
S.dataOne.set(1).get();
window.report = (data_name, data) => {
S[data_name].set(data);
}
当跟改数据时,虽然 swop 对榜单数据的值直接变动也能监听,但是正确的做法应该通过 set 方法来赋值。
S.dataOne.subscribe(new_value => {
...
})
S.get_all_data().dataOne = 1;
subscribe
subscribe 方法会对绑定数据进行监听,返回一个 remove 函数,用于注销当前的监听。
S.create('dataOne');
const remove = S.dataOne.subscribe((new_value, old_value) => {
document.body.innerHTML = new_value;
this.setState({
xx: new_value,
})
}, true);
remove();
unsubscribe
subscribe 方法会对绑定数据进行监听,需要手动一个个的注销掉监听,你会不会觉得太麻烦呢?unsubscribe 就是一个可以省事的 api,返回值为this
。
S.dataOne.unsubscribe();
polling
polling 会不停的对客户端进行 call,以此更新当前绑定数据的值,返回值为 stop 函数,用于终止轮询。
let interval;
function polling () {
const get = setInterval(async _ => {
const data = await S.call('interface', data);
S.dataOne.set(data);
if (S.get_funs().length < 50) { return; }
interval = setInterval(_ => {
if ( S.get_funs().length < 50) {
clearInterval(interval);
interval = null;
polling();
}
}, 100)
}, 100)
}
polling();
const stop = S.dataOne.polling('interfaceOne', '', data => {
...
});
stop();
S.dataOne.subscribe(new_value => {
this.setState({
xx: new_value,
})
})
S.dataOne.polling();
polling 方法在内部没有采用定时器的方法来轮询,所以不会带来大的内存开销。当返回的数据没有变动时候,polling 方法会降低轮询的频率,减少运行时的开销,关于清除所有绑定属性的轮询,可以看这里 clear_polling。
约定
swop 使用约定好的数据格式与客户端进行交互,这需要客户端的开发者配合。
在 JavaScript 层面,swop 会把数据转换成
{
origin_data: xxx(真正需要发送的数据),
id: xxx(swop 生成的一段随机字符串),
}
相应的,响应数据也应该保证统一的格式
{
origin_data: xx(真正需要响应的数据),
id: xx(swop 生成的id,id 是必须的),
}
id 和 origin_data 是唯一约定好的字段名,不应该带有其他的数据字段,不同的是,响应数据的格式是需要客户端的开发者手动转换成我们需要的格式,而 swop 会帮 JavaScript 开发者来做转换,id 是两者之间通信的凭证,swop 必须依靠 id 才能找到相应的响应集合。
如果响应数据的格式是 json,但在初始化实例的时候并没有让 swop 做 json 的解析,那么 swop 会通过正则表达式来截取真正需要的数据,需要注意带来的运行时开销。
绑定数据的更新也不应该通过 polling 函数来实现更新,客户端应该在数据发生变化时,数据上报给 JavaScript,通过绑定属性的 set 方法实现更新.