socket和公共狀態管理連接方案(vuex,redux和小程序)
我們都知道在vue和react這種單頁面組件化項目中,建立socket連接會遇到,重複連接,切換組件連接中斷等問題,而且如果想要在任何頁面接受到來自socket傳遞的信息,所以在建立socket連接時候就要考慮是否要把連接實例化放在公共state裏邊統一管理,這樣可以方便在任何組件中調用socket方法。這裏會介紹socket與Vuex和redux進行連接實時接受信息改變數據的方案。
websocket與公共管理邏輯圖
本方案的大體思路就是如上圖所示,現在頁面初始化的時候根據需要向vuex或者redux發起dispatch觸發初始化的方法,初始化的時候觸發websocket,js構造函數或者類的實例,並且要把改變公共狀態方法的commit作爲參數傳遞給socket實例 , 而真正建立起socket連接的方法實在webosocket實例中進行的,websocket實例會暴露出兩個方法,一個subscribe用來監聽服務端傳遞的信息來改變管理狀態,當然這裏的觸發是根據調用commit 函數來觸發的,另一個是又任意組件調用的emit方法 ,來把信息傳遞給服務端,從而實現了雙向通信,並把通信回執內容放在公共狀態管理,避免切換組件信息丟失,重新連接,丟失連接等情況發生。下面會拿vuex例子具體講一下流程
websocket與vue及vuex案例
以上就是文件的格式(這裏簡化了), websocket.js就是socket方法集裏邊又訂閱器,發佈器,失敗調度,心跳機制 , vuex下邊的socket,js就是一個vuex 在redux中就是一個reducer,
socket.vue就是要用到socket連接的組件,廢話不說,下面一一解釋
首先我們來看socket初始化
if (!socket.ws) {
//在socket.vue文件中初始化socket連接
this.$store.dispatch('socketInit')
}
這是隻是單獨觸發了一個dispatch 在調用了一個socketInit方法,然後我們來看vuex中socket.js中的socketInit方法
import Socket from '../websocket' //socket 方法類
import socketAction from '../../config/socket' //這個是對服務端的數據處理的中間件函數,這裏可以忽略
export default {
state: {
ws: null, // websorket實例
},
mutations: {
subscribe_socket (state,{data}){
//這裏的data爲socket連接後端返回來的數據
},
contentSocket (state, { commit }) {
state.ws = new Socket(commit, socketAction)
}
},
actions: {
// 創建實例
socketInit ({commit, state}) {
commit('contentSocket', { commit }) //把commit作爲參數
}
}
}
在vuex的異步函數actions調用了初始化的方法,然後把觸發contentSocket 發法來創建實例,並綁定在state上的ws上,這裏一定要把commit 來作爲參數,一邊socket實例能觸發方法改變state,
我們知道了socket實例如何綁定和commit傳遞的了 ,下面我們看看websocket.js文件怎麼運作的了
function socket (commit, actions) {
if (isType(commit) !== 'Function') {
throw new Error('commit must be a function')
}
this.commit = commit //觸發vuex中mutations的commit
this.actions = actions || null
this.timer = null
this.errorResetNumber = 0 // 錯誤重連間隔
this.closeWs = false
this.errorFrom = 0 // socket斷開來源
this.errorResetTimer = null // 錯誤重連輪詢
this.errorDispatchOpen = true // 開啓錯誤調度
this.heartSocketOpen = false
isSocketContent()
this.$soctket_init()
}
我們看到了websocket函數是一個構造函數用來做初始化操作, isSocketContent()是用來獲取token等操作大家不必在意, 這裏觸發了一個soctket_init()方法
socket.prototype.$soctket_init = function (callback) {
const _this = this
if (_this.closeWs) {
throw new Error('socket is closed ,$socker_init is fail , all methods is invalid')
}
const token = window.localStorage.getItem('token') || window.sessionStorage.getItem('token') || null
if (!token) {
throw new Error('token is underfined')
}
const handerErrorMachine = () => {
if (_this.errorResetNumber === 4) {
_this.errorResetNumber = 0
_this.errorResetTimer = null
_this.errorFrom = 0
_this.errorDispatchOpen = false
_this.ws = null
console.log('socket連接失敗')
return
}
_this.errorResetTimer = setTimeout(() => {
_this.$soctket_init()
_this.errorResetNumber++
}, _this.errorResetNumber * 2000)
}
const errorDispatch = (eventment) => { //錯誤調度
let event = eventment
return function () {
if (_this.errorFrom === 0 && _this.errorDispatchOpen) {
_this.errorFrom = event
}
event === 1 ? console.log('web socket has failed from closeState ') : console.log('web socket has failed from errorState ')
if (_this.errorFrom === event && !_this.closeWs) {
_this.errorResetTimer && clearTimeout(_this.errorResetTimer)
handerErrorMachine()
}
}
}
if (this.timer) clearTimeout(this.timer)
_this.ws = new WebSocket(socketUrl + '?token=' + token) //這裏才進行了真正的socket連接
_this.ws.onopen = function () {
callback && callback()
_this.errorResetNumber = 0
_this.errorResetTimer = null
_this.errorFrom = 0
_this.errorDispatchOpen = true
_this.$soctket_subscribe()
_this.$soctket_heartSoctket()
console.log('web socket has connected ')
}
_this.ws.onclose = errorDispatch(1)
_this.ws.onerror = errorDispatch(2)
}
這裏纔是真正的socket連接 和一些錯誤處理方式 , 這裏把socket連接和構造函數中的ws綁定在一起,以及一個連接失敗的調度機制 , 裏邊有一個之前一直提到的方法,.soctket_heartSoctket() 是一個心臟搏動機制,我們知道如果socket連接長時間沒有通話會自動斷開連接,所以這裏有一個心臟搏動機制。接下來我們看一下,soctket_subscribe 方法
subscribe訂閱器
/**
* 訂閱器->接受廣播
*/
socket.prototype.$soctket_subscribe = function () {
const _this = this
_this.ws.onmessage = function (res) {
if (_this.actions) {
if (isType(_this.actions) !== 'Function') {
throw new Error('actions')
} else {
_this.commit(..._this.actions(res.data))
}
} else {
_this.commit(res.data)
}
_this.$soctket_heartSoctket()
}
}
我們纔看到原來之前vuex傳進來的 commit 在這裏發揮了作用,也就是觸發mutations 來改變state裏邊 的數據 ,來重新渲染試圖 ,接下來我們看一下emit觸發器
emit觸發器
/**
* 觸發器->發佈信息
* @param callback 狀態處理
* @param value 數據處理
*/
socket.prototype.$soctket_emit = function (value, callback) {
const _this = this
const poll = function () {
return _this.ws.readyState
}
if (callback && isType(callback) !== 'Function') {
throw new Error('$socket_emit arugment[1] must be a function')
}
if (!_this.ws) {
throw new Error('$socket dispatch is fail please use $socket_open method')
}
if (_this.ws.readyState === 1) { // 連接成功狀態
_this.ws.send(value)
_this.$soctket_heartSoctket()
callback && callback()
}
else if (_this.ws.readyState === 0) { // 連接中狀態 ,輪詢查詢連接
eventPoll(poll, 1, 500, () => {
_this.ws.send(value)
_this.$soctket_heartSoctket()
callback && callback()
})
}
else { // 失敗重新連接
_this.$soctket_init(() => {
_this.$soctket_emit(value, callback)
})
}
}
這個就是之前提到的emit 觸發器 用來在vue中調用, 來向服務端發起數據通信,就實現了雙向的數據通信, 裏邊有一個輪詢器 來輪詢eventPoll ,websocket 的狀態是否是已經連通的狀態
,那麼在Vue文件中是怎麼調用emit的呢 ,很簡單就是調用vuex中之前綁定的state裏邊的wx
const { ws } = this.$store.state.socket
ws.$soctket_emit(JSON.stringify({
data: 'hello , world'
}), () => {
console.log('發送成功')
})
就是這麼簡單觸發的。以上整個機制都已經講解了一邊,那麼還有心跳機制,給大家介紹一下
heart心跳機制
/**
* 心臟搏動機制->防止斷開連接
*/
socket.prototype.$soctket_heartSoctket = function () {
if (this.timer) clearTimeout(this.timer)
console.log(this.timer)
this.timer = setTimeout(() => {
if (this.ws.readyState === 1 || this.ws.readyState === 0) {
this.ws.send('heart , socket')
this.$soctket_heartSoctket()
} else {
this.$soctket_init()
}
}, 59000)
就是不斷向服務端發起消息,來防止斷開連接。
還有兩個方法來控制ws的連接和關閉
/**
* 開啓,關閉 socket
*/
/**
* 關閉socket連接
*/
socket.prototype.$soctket_close = function () {
if (this.timer) clearTimeout(this.timer)
if (this.errorResetTimer)clearTimeout(this.errorResetTimer)
this.closeWs = true
this.ws.close()
}
/**
* 重啓socket連接
*/
socket.prototype.$soctket_open = function () {
if (!this.closeWs) {
throw new Error('socket is connected')
}
this.timer = null
this.errorResetNumber = 0
this.closeWs = false
this.errorFrom = 0
this.errorResetTimer = null
this.errorDispatchOpen = true
this.heartSocketOpen = false
this.closeWs = false
this.$soctket_init()
}
小程序的socket連接 ,
小程序的socket連接和h5 的差不多一個體系,也是用公共管理進行連接 , 不過commit的傳遞方式和h5有點出入,這裏就不解釋了,這套體系在項目中還是比較穩定的**,喜歡的朋友歡迎來到gitHub上下載源碼
你可以在github上找到源碼[here][1].
鏈接: [link]https://github.com/laoxiedabaojian/websocket-vue-react-.git
https://github.com/laoxiedabaojian/websocket-vue-react-
謝謝大家~~~~