以前的约定--前端小纠结--Vue项目代码组织和风格约定javascript
本文产生的背景是,由于在封装Axios
的时候,对于服务端的异常或者须要根据业务上异常code,进行不一样的处理逻辑。可是基于Axios
作的封装做为跨项目、跨框架模块使用因此不能和具体的router或者store等模块耦合,因此使用Event Bus
,在整个Web
范围中来解耦各个组件。html
Event Bus有特殊的使用场景,不止在view组件之间的通讯使用;Event Bus设计做为整个SPA应用的事件(消息)投递层使用。前端
模块通讯vue
解决模块之间的通讯问题,view组件层面,父子组件、兄弟组件通讯均可以使用event bus处理。html5
模块解耦java
storage change事件,cookie change事件,view组件的事件等,所有转换ios
使用Event Bus来订阅和发布,这样就统一了整个应用不一样模块之间的通讯接口问题。git
父子页面通讯github
window.postMessage
+ Event Busweb
多页面通讯
storage change + Event Bus
参考
jQuery
和Vue
方法: on
, off
, once
, emit(可选trigger/dispatch/publish)
vue
框架,就使用Vue
对象或者使用dynamic-vue-bus包(其实也是包装了下Vue,实现自动destroy handler)。PubSubJS
封装。BusEvent
元数据模型设计参考DOM中的Event对象
// 服务端返回的元数据模型(responeBody)
export interface ResponseResult<T = any> {
message: string;
code: number;
data: T;
}
export interface BusEvent<T = any> extends ResponseResult<T> {
type: string;
}
export type BusEventHandler = (data: BusEvent) => any;
复制代码
这里的
message
是参考Error来设计的,由于当咱们程序异常或者业务上异常时,就能够统一直接使用new Error()
进行处理。
项目中实现选择一种便可。
Vue
封装实现import EventBus from 'dynamic-vue-bus';
import { isFunction } from 'lodash-es';
// 基于Vue实现
export const VueBus = {
originBus: EventBus,
on(topic: string | string[], handler: BusEventHandler): string[] {
// @ts-ignore
this.originBus.$on(topic, handler)
return [];
},
off(topic: string | string[], handler?: any) {
const length = arguments.length;
switch (length) {
case 1:
this.originBus.$off(topic);
break;
case 2:
this.originBus.$off(topic, handler);
break;
default:
this.originBus.$off();
}
},
once(topic: string, handler: BusEventHandler) {
// @ts-ignore
return this.originBus.$once(topic, handler);
},
publish(topic: string, data: BusEvent) {
return this.originBus.$emit(topic, data);
},
};
复制代码
使用:
function wsHandler(evt: BusEvent) {
console.log(evt);
}
const topic = 'ws.100021';
VueBus.on(topic, wsHandler);
// VueBus.once(topic, wsHandler);
VueBus.publish(topic, {
code: 100021,
type: 'ws',
message: '',
data: {
result: [{
id: 1,
name: 'junna'
}]
}
});
// VueBus.off();
VueBus.off(topic);
// VueBus.off(topic, wsHandler);
// 使用原生的Vue
const eventBus = PubSubBus.originBus;
复制代码
PubSubJS
实现如下代码并没测试
export const PubSubBus = {
originBus: PubSub,
on(topic: string | string[], handler: BusEventHandler): string[] {
if (!topic) {
return [];
}
// @ts-ignore
let events: string[] = [].concat(topic);
return events.map(evt =>
PubSub.subscribe(evt, () => (topic: string, data: BusEvent) =>
handler(data)
)
);
},
/** * 不兼容这种模式:PubSubBus.off('ws.001', handler); * 由于PubSubJS使用token来off这种操做 */
off(tokenOrHandler?: () => void | string | string[]) {
let length = arguments.length,
evts: string[],
listener;
if (length === 0) {
PubSub.clearAllSubscriptions();
} else {
// PubSubBus.off(handler);
if (isFunction(tokenOrHandler)) {
PubSub.unsubscribe(listener);
} else {
// @ts-ignore
evts = [].concat(tokenOrHandler);
evts.forEach(evt => PubSub.unsubscribe(evt));
}
}
},
once(topic: string, handler: BusEventHandler) {
// @ts-ignore
return PubSub.subscribeOnce(topic, handler);
},
publish(topic: string, data: BusEvent, sync = false) {
return sync ? PubSub.publishSync(topic, data) : PubSub.publish(topic, data);
},
};
复制代码
使用:
function wsHandler(evt: BusEvent) {
console.log(evt);
}
const topic = 'ws.100021';
const tokens: string[] = PubSubBus.on(topic, wsHandler);
// PubSubBus.once(topic, wsHandler);
PubSubBus.publish(topic, {
code: 100021,
type: 'ws',
message: '',
data: {
result: [{
id: 1,
name: 'junna'
}]
}
});
// PubSubBus.off();
PubSubBus.off(topic);
// PubSubBus.off(tokens[0]); // 等价于VueBus.off(topic, wsHandler);
// PubSubBus.off(wsHandler); // 移除多个topic使用的同一个handler.
// 使用原生的PubSubJS
const PubSub = PubSubBus.originBus;
复制代码
BusEvent#type
类型约定事件类型的约定参考:
ws
: WebSocket
事件sessionStorage
, localStorage
ap
: application cacheui
: 界面cmp
: components事件BusEvent#code
范围约定code范围约定只是参考,由于这个约定须要和服务端小伙伴,甚至系统设计时规划决定。 (瞎写)
ws
: 10000~19999sessionStorage
: 22000~22999, localStorage
: 23000~23999ap
: 25000~25999ui
: 42000~42999cmp
: 43000~44999有了约定,就能够统一发布相关的事件
// 模拟WebSocket推送消息
const data = [{
code: 100021,
type: 'ws',
message: '',
data: {
result: [{
id: 1,
name: 'junna'
}]
}
},{
code: 100022,
type: 'ws',
message: '',
data: {
result: [{
id: 1,
name: 'junna'
}]
}
}];
data.forEach(event => PubSubBus.publish(`${event.type}.${event.code}`), event));
复制代码
经过对Event Bus的统一封装,对外提供统一的接口,统一整个SPA事件系统(非DOM层面),完成了模块之间的解耦。
缺点:
Vue
封装实现的不支持namespace
Vue
封装实现和PubSubJS
接口参数和返回值有差别,因此选择一种便可Working with quota on mobile browsers
欢迎加入群聊
若是入群失败,添加我的微信,拉你入群,验证消息:前端交流
关注微信公众号,发现更多精彩内容