欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue Socket.io源碼解讀

 更新時(shí)間:2018年02月07日 14:40:00   作者:Juliiii  
這篇文章主要介紹了Vue Socket.io源碼解讀,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

背景

有一個(gè)項(xiàng)目,今年12月份開始重構(gòu),項(xiàng)目涉及到了socket。但是socket用的是以前一個(gè)開發(fā)人員封裝的包(這個(gè)一直被當(dāng)前的成員吐槽為什么不用已經(jīng)千錘百煉的輪子)。因此,趁著這個(gè)重構(gòu)的機(jī)會(huì),將vue-socket.io引入,后端就用socket.io。我也好奇看了看vue-socket.io的源碼(我不會(huì)說是因?yàn)檫@個(gè)庫的文檔實(shí)在太簡(jiǎn)略了,我為了穩(wěn)點(diǎn)去看源碼了解該怎么用)

開始

文件架構(gòu)


我們主要看src下的三個(gè)文件,可以看出該庫是用了觀察者模式

Main.js

// 這里創(chuàng)建一個(gè)observe對(duì)象,具體做了什么可以看Observer.js文件
let observer = new Observer(connection, store)

// 將socket掛載到了vue的原型上,然后就可以
// 在vue實(shí)例中就可以this.$socket.emit('xxx', {})
Vue.prototype.$socket = observer.Socket;
import store from './yourstore'
Vue.use(VueSocketio, socketio('http://socketserver.com:1923'), store);

我們?nèi)绻褂眠@個(gè)庫的時(shí)候,一般是這樣寫的代碼(上圖2)。上圖一的connection和store就分別是圖二的后兩個(gè)參數(shù)。意思分別為socket連接的url和vuex的store啦。圖一就是將這兩個(gè)參數(shù)傳進(jìn)Observer,新建了一個(gè)observe對(duì)象,然后將observe對(duì)象的socket屬性掛載在Vue原型上。那么我們?cè)赩ue的實(shí)例中就可以直接 this.$sockets.emit('xxx', {})了

// 👇就是在vue實(shí)例的生命周期做一些操作
Vue.mixin({
  created(){
    let sockets = this.$options['sockets']

    this.$options.sockets = new Proxy({}, {
      set: (target, key, value) => {
        Emitter.addListener(key, value, this)
        target[key] = value
        return true;
      },
      deleteProperty: (target, key) => {
        Emitter.removeListener(key, this.$options.sockets[key], this)
        delete target.key;
        return true
      }
    })

    if(sockets){
      Object.keys(sockets).forEach((key) => {
        this.$options.sockets[key] = sockets[key];
      });
    }
  },
  /**
   * 在beforeDestroy的時(shí)候,將在created時(shí)監(jiān)聽好的socket事件,全部取消監(jiān)聽
   * delete this.$option.sockets的某個(gè)屬性時(shí),就會(huì)將取消該信號(hào)的監(jiān)聽
   */
  beforeDestroy(){
    let sockets = this.$options['sockets']

    if(sockets){
      Object.keys(sockets).forEach((key) => {
        delete this.$options.sockets[key]
      });
    }
  }

下面就是在Vue實(shí)例的生命周期做一些操作。創(chuàng)建的時(shí)候,將實(shí)例中的$options.sockets的值先緩存下來,再將$options.sockets指向一個(gè)proxy對(duì)象,這個(gè)proxy對(duì)象會(huì)攔截外界對(duì)它的賦值和刪除屬性操作。這里賦值的時(shí)候,鍵就是socket事件,值就是回調(diào)函數(shù)。賦值時(shí),就會(huì)監(jiān)聽該事件,然后將回調(diào)函數(shù),放進(jìn)該socket事件對(duì)應(yīng)的回調(diào)數(shù)組里。刪除時(shí),就是取消監(jiān)聽該事件了,將賦值時(shí)壓進(jìn)回調(diào)數(shù)組的那個(gè)回調(diào)函數(shù),刪除,表示,我不監(jiān)聽了。這樣寫法,其實(shí)就跟vue的響應(yīng)式一個(gè)道理。也因此,我們就可以動(dòng)態(tài)地添加和移除監(jiān)聽socket事件了,比如this.$option.sockets.xxx = () => ()和 delete this.$option.sockets.xxx。最后將緩存的值,依次賦值回去,那么如下圖的寫法就會(huì)監(jiān)聽到事件并執(zhí)行回調(diào)函數(shù)了:

var vm = new Vue({
 sockets:{
  connect: function(){
   console.log('socket connected')
  },
  customEmit: function(val){
   console.log('this method was fired by the socket server. eg: io.emit("customEmit", data)')
  }
 },
 methods: {
  clickButton: function(val){
    // $socket is socket.io-client instance
    this.$socket.emit('emit_method', val);
  }
 }
})

Emitter.js

Emitter.js主要是寫了一個(gè)Emitter對(duì)象,該對(duì)象提供了三個(gè)方法:

addListener

addListener(label, callback, vm) {
  // 回調(diào)函數(shù)類型是回調(diào)函數(shù)才對(duì)
  if(typeof callback == 'function'){
    // 這里就很常見的寫法了,判斷map中是否已經(jīng)注冊(cè)過該事件了
    // 如果沒有,就初始化該事件映射的值為空數(shù)組,方便以后直接存入回調(diào)函數(shù)
    // 反之,直接將回調(diào)函數(shù)放入數(shù)組即可
    this.listeners.has(label) || this.listeners.set(label, []);
    this.listeners.get(label).push({callback: callback, vm: vm});

    return true
  }

  return false
}

其實(shí)很常規(guī)啦,實(shí)現(xiàn)發(fā)布訂閱者模式或者觀察者模式代碼的同學(xué)都很清楚這段代碼的意思。Emiiter用一個(gè)map來存儲(chǔ)事件以及它對(duì)應(yīng)的回調(diào)事件數(shù)組。這段代碼先判斷map中是否之前已經(jīng)存儲(chǔ)過了該事件,如果沒有,初始化該事件對(duì)應(yīng)的值為空數(shù)組,然后將當(dāng)前的回調(diào)函數(shù),壓進(jìn)去,反之,直接壓進(jìn)去。

removeListener

if (listeners && listeners.length) {
  index = listeners.reduce((i, listener, index) => {
    return (typeof listener.callback == 'function' && listener.callback === callback && listener.vm == vm) ?
      i = index :
      i;
  }, -1);

  if (index > -1) {
    listeners.splice(index, 1);
    this.listeners.set(label, listeners);
    return true;
  }
}
return false;

這里也很簡(jiǎn)單啦,獲取該事件對(duì)應(yīng)的回調(diào)數(shù)組。如果不為空,就去尋找需要移除的回調(diào),找到后,直接刪除,然后將新的回調(diào)數(shù)組覆蓋原來的那個(gè)就可以了

emit

if (listeners && listeners.length) {
  listeners.forEach((listener) => {
    listener.callback.call(listener.vm,...args)
  });
  return true;
}
return false;

這里就是監(jiān)聽到事件后,執(zhí)行該事件對(duì)應(yīng)的回調(diào)函數(shù),注意這里的call,因?yàn)楸O(jiān)聽到事件后我們可能要修改下vue實(shí)例的數(shù)據(jù)或者調(diào)用一些方法,用過vue的同學(xué)都知道我們都是this.xxx來調(diào)用的,所以一定得將回調(diào)函數(shù)的this指向vue實(shí)例,這也是為什么存回調(diào)事件時(shí)也要把vue實(shí)例存下來的原因。

Observer.js

constructor(connection, store) {
  // 這里很明白吧,就是判斷這個(gè)connection是什么類型
  // 這里的處理就是你可以傳入一個(gè)連接好的socket實(shí)例,也可以是一個(gè)url
  if(typeof connection == 'string'){
    this.Socket = Socket(connection);
  }else{
    this.Socket = connection
  }

  // 如果有傳進(jìn)vuex的store可以響應(yīng)在store中寫的mutations和actions
  // 這里只是掛載在這個(gè)oberver實(shí)例上
  if(store) this.store = store;

  // 監(jiān)聽,啟動(dòng)!
  this.onEvent()

}

這個(gè)Observer.js里也主要是寫了一個(gè)Observer的class,以上是它的構(gòu)造函數(shù),構(gòu)造函數(shù)第一件事是判斷connection是不是字符串,如果是就構(gòu)建一個(gè)socket實(shí)例,如果不是,就大概是個(gè)socket的實(shí)例了,然后直接掛載在它的對(duì)象實(shí)例上。其實(shí)這里我覺得可以參數(shù)檢查嚴(yán)格點(diǎn), 比如字符串被人搞怪地可能會(huì)傳入一個(gè)非法的url,對(duì)吧。這個(gè)時(shí)候判斷下,拋出一個(gè)error提醒下也好,不過應(yīng)該也沒人這么無聊吧,2333。然后如果傳入了store,也掛在對(duì)象實(shí)例上吧。最后就啟動(dòng)監(jiān)聽事件啦。我們看看onEvent的邏輯

  onEvent(){
    // 監(jiān)聽服務(wù)端發(fā)來的事件,packet.data是一個(gè)數(shù)組
    // 第一項(xiàng)是事件,第二個(gè)是服務(wù)端傳來的數(shù)據(jù)
    // 然后用emit通知訂閱了該信號(hào)的回調(diào)函數(shù)執(zhí)行
    // 如果有傳入了vuex的store,將該事件和數(shù)據(jù)傳入passToStore,執(zhí)行passToStore的邏輯
    var super_onevent = this.Socket.onevent;
    this.Socket.onevent = (packet) => {
      super_onevent.call(this.Socket, packet);

      Emitter.emit(packet.data[0], packet.data[1]);

      if(this.store) this.passToStore('SOCKET_'+packet.data[0], [ ...packet.data.slice(1)])
    };

    // 這里跟上面意思應(yīng)該是一樣的,我很好奇為什么要分開寫,難道上面的寫法不會(huì)監(jiān)聽到下面的信號(hào)?
    // 然后這里用一個(gè)變量暫存this
    // 但是下面都是箭頭函數(shù)了,我覺得沒必要,畢竟箭頭函數(shù)會(huì)自動(dòng)綁定父級(jí)上下文的this
    let _this = this;

    ["connect", "error", "disconnect", "reconnect", "reconnect_attempt", "reconnecting", "reconnect_error", "reconnect_failed", "connect_error", "connect_timeout", "connecting", "ping", "pong"]
      .forEach((value) => {
        _this.Socket.on(value, (data) => {
          Emitter.emit(value, data);
          if(_this.store) _this.passToStore('SOCKET_'+value, data)
        })
      })
  }

這里就是有點(diǎn)類似重載onevent這個(gè)函數(shù)了,監(jiān)聽到事件后,將數(shù)據(jù)拆包,然后通知執(zhí)行回調(diào)和傳遞給store。大體的邏輯是這樣子。然后這代碼實(shí)現(xiàn)有兩部分,第一部分和第二部分邏輯基本一樣。只是分開寫。(其實(shí)我也不是很懂啦,如果很有必要的話,我猜第一部分的寫法還監(jiān)聽不了第二部分的事件吧,所以要另外監(jiān)聽)。最后只剩下一個(gè)passToStore了,其實(shí)也很容易懂

 passToStore(event, payload){
   // 如果事件不是以SOCKET_開頭的就不用管了
   if(!event.startsWith('SOCKET_')) return

   // 這里遍歷vuex的store中的mutations
   for(let namespaced in this.store._mutations) {
     // 下面的操作是因?yàn)?,如果store中有module是開了namespaced的,會(huì)在mutation的名字前加上 xxx/
     // 這里將mutation的名字拿出來
     let mutation = namespaced.split('/').pop()
     // 如果名字和事件是全等的,那就發(fā)起一個(gè)commit去執(zhí)行這個(gè)mutation
     // 也因此,mutation的名字一定得是 SOCKET_開頭的了
     if(mutation === event.toUpperCase()) this.store.commit(namespaced, payload)
   }
   // 這里類似上面
   for(let namespaced in this.store._actions) {
     let action = namespaced.split('/').pop()

     // 這里強(qiáng)制要求了action的名字要以 socket_ 開頭
     if(!action.startsWith('socket_')) continue

     // 這里就是將事件轉(zhuǎn)成駝峰式
     let camelcased = 'socket_'+event
         .replace('SOCKET_', '')
         .replace(/^([A-Z])|[\W\s_]+(\w)/g, (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase())

     // 如果action和事件全等,那就發(fā)起這個(gè)action
     if(action === camelcased) this.store.dispatch(namespaced, payload)
   }
 }

passToStore嘛其實(shí)就是做兩個(gè)事情,一個(gè)是獲取與該事件對(duì)應(yīng)的mutation,然后發(fā)起一個(gè)commit,一個(gè)是獲取與該事件對(duì)應(yīng)的action,然后dispatch。只是這里的實(shí)現(xiàn)對(duì)mutations和actions的命名有了要求,比如mutations的命名一定得是SOCKET_開頭,action就是一個(gè)得socket_開頭,然后還得是駝峰式命名。

最后

首先,這個(gè)源碼是不是略有點(diǎn)簡(jiǎn)單,哈哈哈,不過,能給你們一些幫助,我覺得也挺好的

然后,就是如果上面我說的有是很對(duì)的,請(qǐng)大家去這里發(fā)issue或者直接評(píng)論吧

最后,源碼的詳細(xì)的注釋在這里

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 詳解Vue路由自動(dòng)注入實(shí)踐

    詳解Vue路由自動(dòng)注入實(shí)踐

    這篇文章主要介紹了詳解Vue路由自動(dòng)注入實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • vue日志之如何用select選中默認(rèn)值

    vue日志之如何用select選中默認(rèn)值

    這篇文章主要介紹了vue日志之如何select選中默認(rèn)值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • Vue3初始化如何調(diào)用函數(shù)

    Vue3初始化如何調(diào)用函數(shù)

    這篇文章主要介紹了Vue3初始化如何調(diào)用函數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • Vue滾動(dòng)到指定位置的多種方式示例詳解

    Vue滾動(dòng)到指定位置的多種方式示例詳解

    當(dāng)容器有滾動(dòng)條時(shí),有時(shí)需要將指定的內(nèi)容自動(dòng)滾動(dòng)到可視區(qū)域,怎么實(shí)現(xiàn)呢,下面小編給大家?guī)砹硕喾N方法實(shí)現(xiàn)Vue滾動(dòng)到指定位置,感興趣的朋友跟隨小編一起看看吧
    2023-08-08
  • 基于Vue3實(shí)現(xiàn)組件封裝的技巧分享

    基于Vue3實(shí)現(xiàn)組件封裝的技巧分享

    這篇文章主要介紹了基于Vue3實(shí)現(xiàn)組件封裝的技巧,本文在Vue3的基礎(chǔ)上針對(duì)一些常見UI組件庫組件進(jìn)行二次封裝,旨在追求更好的個(gè)性化,更靈活的拓展,感興趣的小伙伴跟著小編一起來看看吧
    2024-09-09
  • vue集成一個(gè)支持圖片縮放拖拽的富文本編輯器

    vue集成一個(gè)支持圖片縮放拖拽的富文本編輯器

    這篇文章主要介紹了vue集成一個(gè)支持圖片縮放拖拽的富文本編輯器,幫助大家更好的理解和使用vue框架,感興趣的朋友可以了解下
    2021-01-01
  • vue中使用go()和back()兩種返回上一頁的區(qū)別說明

    vue中使用go()和back()兩種返回上一頁的區(qū)別說明

    這篇文章主要介紹了vue中使用go()和back()兩種返回上一頁的區(qū)別說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • 查看vue版本號(hào)以及vue/cli腳手架版本號(hào)方式

    查看vue版本號(hào)以及vue/cli腳手架版本號(hào)方式

    這篇文章主要介紹了查看vue版本號(hào)以及vue/cli腳手架版本號(hào)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • Vue + Vue-router 同名路由切換數(shù)據(jù)不更新的方法

    Vue + Vue-router 同名路由切換數(shù)據(jù)不更新的方法

    本篇文章主要介紹了Vue + Vue-router 同名路由切換數(shù)據(jù)不更新的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • 巧妙運(yùn)用v-model實(shí)現(xiàn)父子組件傳值的方法示例

    巧妙運(yùn)用v-model實(shí)現(xiàn)父子組件傳值的方法示例

    這篇文章主要介紹了巧妙運(yùn)用v-model實(shí)現(xiàn)父子組件傳值的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-04-04

最新評(píng)論