官网位置:http://puremvc.org/
按接口逐一介绍所作的优化内容吧,没列举的亦可能有改动过,但未作大的改动。
1. ICommand
原版command中execute方法接受的参数是INotification对象,从INotification中可以获取到body, name和type,这种封装导致puremvc在派发消息时,你需要不停地去组装和拆分消息,有点繁琐,所以我将execute的参数改为了不定参数,可传可不传,如果sendNotification时传递了参数,则execute可以直接接受真实的形参而非是从一个被封装的INotification对象中去取Body然后再按键值去获取消息数据
module puremvc {
export interface ICommand extends INotifier {
execute(...args: Array<any>): void;
}
}
// for instance
class TestCommand extends puremvc.SimpleCommand {
execute(x:number, y:number, z:number):void {
console.log("x:" + x, "y:" + y, "z:" + z);
}
}
puremvc.Facade.getInstance().sendNotification("TestCommand", [1, 2, 3]);
// for one argument
puremvc.Facade.getInstance().sendNotification("TestCommand", 0);
// or
puremvc.Facade.getInstance().sendNotification("TestCommand", [0]);
// both ok
2. INotification
这玩意儿被我删了,反正没啥用
3. IMediator
原版puremvc中,View 是通过 listNotificationInterests 方法获取 mediator 关心的消息列表,然后在命令被派发时,通过调用mediator 的 handleNotification 来响应消息,而 handleNotification 中,则必须通过 if else 或 switch 的格式来响应消息,这个机制也太繁琐,所以我对它也进行了重构
我在 mediator 中定义了 notificationInterests:Array<IObserver> 属性,mediator 被注册时,依然会调用 listNotificationInterests 方法,但这个方法并不返回命令列表,取而代之的是,是在这个方法中,可直接调用 handleNotification 来注册命令和回调,handleNotification 会调用 registerObserver 来注册观察者,观察者信息会被保存到 notificationInterests 属性中,我增加了一个 removeNotificationInterests 方法,它在 mediator 被移除时将会调用,被调用时会自动注销所有在 listNotificationInterests 中通过 handleNotification 注册的事件
这个改动终结了 handlerNotification 中 if else 或 switch 的写法,方便了不少,下面是简单的例子
// register mediator
class TestMediator extends puremvc.Mediator {
// 注意返回类型是 void 了
listNotification():void {
this.handlerNotification("TestMessageA", this.$onTestMessageA);
this.handlerNotification("TestMessageB", this.$onTestMessageB);
}
// 响应 TestMessageA
private $onTestMessageA(x:number, y:number, z:number):void {
console.log("x:" + x, "y:" + y, "z:" + z);
}
// 响应 TestMessageB
private $onTestMessageB(a:string):void {
console.log("a:" + a);
}
// 你不需要重写 removeNotificationInterests 方法,它自己会在 onRemove 时帮你移除回调
}
// send messages
puremvc.Facade.getInstance().sendNotification("TestMessageA", [1, 2, 3]);
puremvc.Facade.getInstance().sendNotification("TestMessageB", "yes");
// 不需要写 if else 和 switch 方便多了吧?
4. IView
由于 INotification ,ICommand 和 IMediator 的改动,所以这个类的重构比较大,总的来说有以下几点
a: registerObserver 方法
这个方法原本接受的参数有两个,1是notificationName:string, 2是observer:IObserver ,改动后则接受5个参数
/**
* 首先,参数的写法是参考的flash as3中的EventDispatcher,没办法,as3的事件太好用了
* @receiveOnce: 是否只响应一次,默认为false
* @priority: 优先级,优先响应级别高的消息,值越大,级别越高,默认为1,有这个参数的存在,若你想控制
* 回调函数的执行顺序,则会非常容易
*/
registerObserver(name: string, method: Function, caller: Object, receiveOnce: boolean = false, priority: number = 1): IObserver {
}
b: notifyObservers 方法
这个方法原本只接受一个参数,就是notification:INotification,改动后则接受3个参数
/**
* @name: 命令名字
* @args: 参数列表,可缺省,或为任意类型的数据
* @cancelable: 事件是否允许取消,默认为false
*/
notifyObservers(name: string, args?: any, cancelable: boolean = false): void {
}
值得一说的是 cancelable,改动后的命令是允许被取消的,例子如下
puremvc.Facade.getInstance().registerObserver("TestMessage", handlerA, null, false, 1);
puremvc.Facade.getInstance().registerObserver("TestMessage", handlerB, null, false, 2);
function handlerA(x:number) {
console.log("handler a is executed");
}
function handlerB(x:number) {
console.log("handler b is executed");
// 中断消息
puremvc.Facade.getInstance().notifyCancel();
}
puremvc.Facade.getInstance().sendNotification("TestMessage", 5, true);
// 在这个例子中,只会打印如下消息
"handler b is executed"
// 原因之一是 observer 在被注册时,handlerB 的优先级比 handlerA 高,所以 handlerB 先执行
// 原因之二是 "TestMessage" 在被派发时,指定的 cancelable 是 true,而 handlerB 中调用了notifyCancel 方法,所以消息在 handlerB 响应结束之后,被中断传递了
这里有个需要注意的是,我公开了 registerObserver 方法,但并没有去给命令作确保执行的处理,所以,如果你即注册了命令,又在 mediator中注册了回调,那么,你在 mediator 中使用 notifyCancel 的时候,需要特别注意这个问题,如果你希望命令使终都能被执行,则建议你先注册命令,再注册中介者
c: removeObserver 方法
参考 flash as3 中的 EventDispatcher ,很容易就能想到,removeObserver 变成了下面这个样子了
removeObserver(name: string, method: Function, caller: Object): void {
}
d: $isCanceled:boolean 属性
这个属性是用来实现 notifyCancel 的逻辑的
e: $observers:Array<boolean | IObserver > 属性
这个数组,在原版puremvc中,是 Array<IObserver>,为什么我要加上 boolean 呢?因为原版的puremvc中,每次执行 notifyObserver 时,都会对 objservers 进行复制,但如果消息在派发过程中,没有任何方法被注册或注销的话,observers 属性就不会发生变化,这时这个复制就是多余的
所以,我对它这部分作了性能优化,我在 Array[0] 中保存了一个 boolean 默认为 false ,在 notifyObserver 被调用时,我会将这个 boolean 赋值为 true ,执行完毕后,再重置为 false ,当 registerObserver 或 removeObserver 被调用时,我先读取 Array[0] 的值,若为 true ,则说明这个消息正在执行,此时 notifiyObserver 和注册注销会形成干扰,这时候就对 $observers 数组进行复制,复制完再将新的数组中的 Array[0] 重置为 false ,因为后面的注销和注册行为操作的是新数组,己不会再对本次的 notifyObserver 产生干扰了。
这步优化可以省去很多数组复制的操作
5 IFacade
由于上述的改动,外观类中的接口当然也需要一起被改变,不过我在 Facade 中还开放了 registerObserver 和 removeObserver 方法,开放这两个方法的原因,命令派发虽然可以从任何地方发起,但命令的响应除了 ICommand 之外,却只能存在于 mediator 中,这种设定迫使不得不去定义一些本来没必要存在的命令或中介者,带来的影响除了性能的下降之外,还有额外增多的代码工作量,基于此,有些人选择放弃了puremvc,还有些人选择写一个全局的事件派发器,而我选择了重构puremvc /手动滑稽
附git地址,欢迎下载使用:https://github.com/syfolen/ts-puremvc main 分支
结语:遵重原著版权,puremvc版权归原作者 Frederic Saunier 所有,若有侵犯,烦告知
PureMVC Standard Framework for TypeScript - Copyright © 2012 Frederic Saunier
PureMVC Framework - Copyright © 2006-2012 Futurescale, Inc.