手寫程式碼之 【釋出訂閱】

Hi-Sen發表於2020-11-05

前言

在目前比較火熱的前端主流框架中,相信大家都已經預設是三大家族 Vue , React , Angular 了 ,談到它們呢,無疑離不開一個關鍵詞 元件

為什麼這麼說:因為在它們的設計模式中,都希望是以一個個細小的元件來組成一個大的元件,而這個大的元件就是我們要寫的前端頁面了, 說到這裡,也許已經有童鞋知道我要表達什麼意思了,其實我就是想說:在一個前端工程中,存在著很多這樣的元件,那麼這些元件都有著複雜的關係 ,如 巢狀,相鄰,不相關 等 , 他們如何通訊呢 ? okey ,我們通過 Vue , React 來簡單分析一下

Vue
父子:props
子父:$emit , $on 釋出訂閱(元件預設的父子之間的觸發場景)
跨層級:provider / inject
兄弟:可能是尋找功能的父元件
通用:vuex , 釋出訂閱,eventBus(new 一個 vue 例項,利用本身已有 $on 和 $emit ,做點對點通訊)

React
父子:props
子父:通過回撥的方式
跨層級:context
兄弟:也可能是尋找共同的父元件
通用:Redux ,釋出訂閱 eventBus (自己手寫)

通過簡單的分析,我們可以看出來,其實元件之間的通訊也是大同小異,基本常用的就這些東西,相信在大家的實際開發中遇到的最多就是元件通訊了,在這裡呢,我們就手寫一個通用方式【釋出訂閱】

首先,什麼是釋出訂閱 ? 通俗一點說就是一個釋出通知,一個接收通知做對應的事兒 , 來實踐一下

先定義一個類 EventBus

class EventBus {
  constructor() {
    this.events = this.events || {};
  }
}

定義一個釋出者

// 掛載在類 EventBus 的原型上
// type : 釋出的事件型別,也可以說是事件名稱
// args : 攜帶引數
EventBus.prototype.emit = function (type, ...args) {
  const e  = this.events[type]; // 返回一個陣列
  if (e instanceof Array) {
	  e.forEach( c => {
	  	// 改變 this 呼叫對應的釋出事件
	  	 c.apply(this, args);
	  })
  }
};

定義一個訂閱者

// type : 訂閱的事件型別,也可以說是事件名稱
// 回撥函式 : dosomething 
EventBus.prototype.on = function (type, fun) {
  const e = this.events[type]; // 同上
  if (!e) {
  	// 如果沒有該訂閱事件,則自定新增,並賦值對應的操作
    this.events[type] = [fun];
  } else {
    e.push(fun);
  }
};

定義一個移除事件

// 移除對應的事件
EventBus.prototype.off = function (type) {
  delete this.events[type];
};

實踐:

// new 一個例項
const ev = new EventBus();
//訂閱 fn 事件 , 並接收引數
ev.on("fn",(args) => {console.log("訂閱到了 fn 事件",args)})
// 釋出 fn 事件 , 並傳測試引數
ev.emit("fn",{arg:"測試引數"})
//移除 fn 事件
ev.off("fn")
// 列印看結果
console.log(ev);

相關文章