以前的約定--前端小糾結--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
歡迎加入羣聊
若是入羣失敗,添加我的微信,拉你入羣,驗證消息:前端交流
關注微信公衆號,發現更多精彩內容