文章大綱
已經很習慣串接 restful api,突然有個客戶說他的遊戲網站所有資料傳輸都是用 websocket,也就說一般我們打 api,會有開始送出 request 、結束收到 response,是一個有時間性的過程;但是如果是 websocket 的話,送出跟收到是無法放在同一個 function 裡等待(確保)他完成的。websocket 一但連線並開始監聽,就會隨時收到來自 server 的訊息,並不一定能代表這個回傳結果一定就是來自你剛剛送出的請求。
菜鳥如我上網找了很多方法,然後又因為不想要在每個頁面都要寫一次監聽的 function,所以我就異想天開把監聽的步驟放在App.vue
,然後只要收到資訊就把資料送到 vuex,在每個頁面去 watch vuex state 的變化~(雖然這樣好像還是要監聽,但是總覺得這樣寫法比較 vue 一點?XD)
首先,要先有websocket連線的底層。
建立一個檔案叫做socket.js
import Vue from "vue";
const wsUrl = process.env.VUE_APP_API_URL;
var socket = new WebSocket(wsUrl);
const emitter = new Vue({
methods: {
send(message) {
if (1 === socket.readyState) socket.send(JSON.stringify(message));
},
connect() {
socket = new WebSocket(wsUrl);
socket.onmessage = function(msg) {
emitter.$emit("message", msg.data);
};
socket.onerror = function(err) {
emitter.$emit("error", err);
};
socket.onclose = function() {
emitter.connect();
};
}
}
});
emitter.connect();
export default emitter;
emitter
這個東西是方便之後我們在vue
裡面傳送資料給 server 的寫法。
然後因為我遇到的是只要登出後,後端會自己把我的websocket
連線斷開,導致我不刷新頁面的話,就無法打傳送資料,所以在onclose
那邊才會又重新連線。
在 vuex
中準備一個 state
去接 websocket
的資料
新增一個檔案 store/ws.js
,記得要在store/index.js
裡面引用進去。
export const state = {
wsRes: {}
};
export const actions = {};
export const mutations = {
setWsRes(state, payload) {
state.wsRes = payload;
}
};
export const getters = {};
export default {
state,
actions,
mutations,
getters,
namespaced: true
};
import Vue from "vue";
import Vuex from "vuex";
import ws from "./ws";
Vue.use(Vuex);
export default new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: { ws }
});
在App.vue
中全局監聽websocket
,並且收到資料後馬上傳入vuex
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
import Socket from "@/utils/socket";
import { mapMutations } from "vuex";
export default {
name: "App",
created() {
Socket.$on("message", this.handleGetMessage);
},
beforeDestroy() {
Socket.$off("message", this.handleGetMessage);
},
methods: {
...mapMutations({
setWsRes: "ws/setWsRes",
}),
handleGetMessage(msg) {
// 一些全局的動作可以放在這裡
this.setWsRes(JSON.parse(msg));
}
}
};
</script>
如果你有一些比如收到錯誤碼要登出這種全局的東西,就可以寫在handleGetMessage
裡面~這樣就不用在每個vue
頁面去判斷。
重頭戲:在頁面中使用
<template>
<div>{{ name }}</div>
</template>
<script>
import { mapState } from "vuex";
import Socket from "@/utils/socket";
export default {
name: "Home",
data() {
return {
name: "",
};
},
computed: {
...mapState({
wsRes: state => state.ws.wsRes
}),
},
watch: {
wsRes(val) {
const { StatusCode, name } = val;
StatusCode === 0 && this.$message({ type: "error", message: "查詢失敗" });
if (list) {
this.name = name;
}
}
},
};
</script>
這樣就可以隨時檢查是否後端有傳新的name
,然後直接顯示在畫面上。
如果是要傳送資料的話,要使用send
方法:
methods: {
handleGetName() {
Socket.send(
//...一些後端要求要傳的資料request,通常會是一包物件{}。
);
}
}
沒錯!真的這樣就可以了,因為上面我們已經用watch
監聽結果了,這邊就只要專注在送出資料就可。
發表評論
想要加入討論嗎?請盡情發表您的想法!