JS利用中介模式開發(fā)全局控制器
中介模式
中介模式定義了一個單獨的(中介)對象,來封裝一組對象之間的交互。將這組對象之間的交互委派給與中介對象交互,來避免對象之間的直接交互。
在實際的項目中,程序由許多對象組成,對象間的交流錯綜復雜。
隨著應用程序的規(guī)模增大,對象越來愈多,他們之間的關系也越來復雜。對象間很容易出現(xiàn)相互引用而導致程序無法運行。同時開發(fā)者需要改變或者刪除某一個對象時候,需要查找并且改造所有引用到它的對象。這樣一來,改造的成本會變的非常高。
但中介者模式可以讓各個對象之間得以解耦。
之前的場景下,如果 A 發(fā)生了改變,開發(fā)者需要修改 B、D、E、F 4 個對象,而通過中介者模式,我們只需要修改中介者這一個對象即可。
中介者的好處是簡化了對象之間的交互,壞處則是中介類有可能會變成大而復雜的“上帝類”(God Class)。所以,在使用中介者模式時候,設計上一定要非常克制。
實際使用
在前端項目開發(fā)的過程中,有很多業(yè)務無關的功能,但這些功能會散落在各個業(yè)務中,難以管理,我們利用中介者模式的思想來構建一個的控制器。
基礎控制器
// axios 請求工具 import axios from "axios"; // mitt 微型發(fā)布訂閱工具 import mitt from "mitt"; const createApi = ($controller) => { const api = axios.create({ baseURL: "/api", timeout: 10000, }); api.interceptors.request.use(() => { // 可以通過 $controller.tmpCache 緩存一些數(shù)據(jù) }); return api; }; const createBus = () => mitt(); class Controller { // 臨時緩存,也可以添加更復雜的緩存 tmpCache: Record<string, any> = {}; // 事件總線 bus = createBus(); constructor() { this.api = createApi(this); } static instance: Controller | null = null; static getInstance() { if (!Controller.instance) { Controller.instance = new Controller(); } return Controller.instance; } } export default Controller;
此時控制器中有一個極簡的緩存 tmpCache,發(fā)布訂閱工具。以及服務端請求方法 api。開發(fā)者只需要導入 Controller 類即可使用。如果后續(xù)需要更加復雜的緩存或者改造 api(如切換為 fetch)請求方法。開發(fā)者可以很快的進行替換。而無需改造對應文件。
import axios from "redaxios"; const createApi = ($controller) => { const api = axios.create({ baseURL: "/api", }); const getOld = api.get; api.get = (...params) => { // 如果出發(fā)緩存直接返回 // if (xxx) { // return $controller.tmpCache // } return getOld(...params); }; return api; };
添加用戶類
登錄的用戶信息以及對應操作可以說是對象交互的核心,將其放入控制器中。
class User { readonly $controller: Controller; user: User | null = null; constructor($controller: Controller) { this.$controller = $controller; } getData(key?: string) { return key ? this.user[key] : this.user; } // 登錄 login(params) { $controller.api.post("/login", params).then((response) => { // 處理授權以及 user 信息 }); } // 退出 logout() { $controller.api.post("/logout").then(() => { // 清理對應數(shù)據(jù) $controller.tmpCache = {}; this.user = null; }); } // 設置用戶配置 setSetting(params) { return $controller.api.post("/setting", params).then(() => { this.user.setting = params; }); } } class Controller { constructor() { this.api = createApi(this); this.user = new User(this); } logout() { this.user.logout(); } }
此時控制器已經(jīng)具備前端開發(fā)中“大部分”功能了,如數(shù)據(jù)緩存,數(shù)據(jù)請求,登錄用戶信息處理等。大部分情況下,中介類也基本夠用了。至于其他的工具類,除非 80% 的業(yè)務都會用到,否則不建議添加到此類中去。但對于這些工具做一層簡單的封裝也是必要的(考慮后續(xù)成本)。
添加業(yè)務類
此時控制器有了用戶信息,多種緩存,請求方法等。我們可以通過注入來實現(xiàn)其他業(yè)務。
class BasicService { constructor($controller) { this.api = $controller.api; this.bus = $controller.bus; } } class OrderService extends BasicService { static xxxKey = "xxxx"; constructor($controller) { super($controller); // 使用全局緩存 this.cache = $controller.xxxCache; // 或者構建一個新的 cache 掛載到 controller 上 this.cache = $controller.getCreatedCache("order", XXXCache); } getOrders = async () => { if (this.cache.has(OrderService.xxxKey)) { return this.cache.get(OrderService.xxxKey); } let orders = await this.api.get("xxxxx"); // 業(yè)務處理,包括其他的業(yè)務開發(fā) order = order.map(); this.cache.set(OrderService.xxxKey, orders); // 可以發(fā)送全局事件 this.bus.emit("getOrdersSuccess", orders); return orders; }; clear() { // 如果構建一個新的緩存對象,直接 clear 即可 this.cache.clear(); this.cache.delete(OrderService.xxxKey); } } class Controller { // 臨時緩存,也可以添加更復雜的緩存 tmpCache: Record<string, any> = {}; // 事件總線 bus = createBus(); services: Record<string, BasicService> = {}; getService(serviceCls) { // 同一個類 name 為相同的名稱 const { name } = serviceCls; if (!this.services[name]) { this.services[name] = new serviceCls(Controller.instance); } return this.services[name]; } }
開發(fā)者可以在具體的代碼中這樣使用。
import Controller from "../../controller"; import OrderService from "./order-service"; // A 頁面 const orderService = Controller.getInstance().getService(OrderService); const priceService = Controller.getInstance().getService(PriceService); // B 頁面都會使用同一個服務 const orderService = Controller.getInstance().getService(OrderService);
有同學可能會奇怪,為什么 Controller 不能直接導入或者自動注入 OrderService 呢。這樣使用的原因是往往 Controller 和 BasicService 在基礎類中,具體的業(yè)務類分布在各個業(yè)務代碼中。
這里沒有使用任何依賴注入的框架,原因是因為如果引入了依賴注入會大大增加復雜度。同時前端的業(yè)務都是分散的,沒有必要都放在一起。在組件需要使用到服務候才將其裝載起來。
以上就是JS利用中介模式開發(fā)全局控制器的詳細內(nèi)容,更多關于JS中介模式全局控制器的資料請關注腳本之家其它相關文章!
相關文章
JavaScript setTimeout與setTimeinterval使用案例詳解
這篇文章主要介紹了JavaScript setTimeout與setTimeinterval使用案例詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-08-08利用JS解決ie6不支持max-width,max-height問題的方法
本篇文章主要介紹了利用JS解決ie6不支持max-width,max-height問題的方法。需要的朋友可以過來參考下,希望對大家有所幫助2014-01-01表單提交前觸發(fā)函數(shù)返回true表單才會提交
這篇文章主要介紹了表單提交前觸發(fā)函數(shù)當返回true表單才會提交的具體實現(xiàn),需要的朋友可以參考下2014-03-03Wordpress ThickBox 點擊圖片顯示下一張圖的修改方法
Wordpress自帶的ThickBox特效中,單擊圖片會調(diào)用 tb_remove 以關閉特效窗口,現(xiàn)將修改其動作為顯示下一張圖。2010-12-12