Vuex 模块中的 Websocket:尝试使用 Vuex rootstate 时出现异步问题
Websocket within Vuex module: Async issue when trying to use Vuex rootstate
我正在尝试以尽可能模块化的方式使用来自 websocket 的数据填充我的应用程序,尝试使用最佳实践等。这很难,因为即使我已经非常深入地寻求有关使用 websockets 的建议/ Vuex 和 Vue 我仍然找不到完成这项工作的模式。来回之后,我决定使用商店来管理 websocket 的状态,然后使用该 vuex 模块填充其他组件的状态,基本上是一个聊天队列和一个聊天小部件,因此需要实时使用 websockets通讯.
这是 websocket 商店。如您所见,我正在将 processWebsocket 函数转换为承诺,以便在其他模块存储操作中使用 async/await。我看到这个工作的方式(我可能错了,所以请随时纠正我)是所有将使用 websocket 模块状态的组件将等到状态准备好然后使用它(这是目前不工作):
export const namespaced = true
export const state = {
connected: false,
error: null,
connectionId: '',
statusCode: '',
incomingChatInfo: [],
remoteMessage: [],
messageType: '',
ws: null,
}
export const actions = {
processWebsocket({ commit }) {
return new Promise((resolve) => {
const v = this
this.ws = new WebSocket('xyz')
this.ws.onopen = function (event) {
commit('SET_CONNECTION', event.type)
v.ws.send('message')
}
this.ws.onmessage = function (event) {
commit('SET_REMOTE_DATA', event)
resolve(event)
}
this.ws.onerror = function (event) {
console.log('webSocket: on error: ', event)
}
this.ws.onclose = function (event) {
console.log('webSocket: on close: ', event)
commit('SET_CONNECTION')
ws = null
setTimeout(startWebsocket, 5000)
}
})
},
}
export const mutations = {
SET_REMOTE_DATA(state, remoteData) {
const wsData = JSON.parse(remoteData.data)
if (wsData.connectionId && wsData.connectionId !== state.connectionId) {
state.connectionId = wsData.connectionId
console.log(`Retrieving Connection ID ${state.connectionId}`)
} else {
state.messageType = wsData.type
state.incomingChatInfo = wsData.documents
}
},
SET_CONNECTION(state, message) {
if (message == 'open') {
state.connected = true
} else state.connected = false
},
SET_ERROR(state, error) {
state.error = error
},
}
当我调试应用程序时,websocket 商店一切正常,我可以看到它的状态,来自服务器的数据在那里等等。当我尝试使用 websocket 填充其他组件属性时,问题就来了。当其他组件需要 websocket 状态时,这还没有准备好,所以我收到错误。这是我的一个组件尝试使用 websocket 状态的示例,我基本上从创建的循环方法中调用一个操作:
<template>
<ul class="overflow-y-auto overflow-hidden pr-2">
<BaseChat
v-for="(chat, index) in sortingIncomingChats"
:key="index"
:chat="chat"
:class="{ 'mt-0': index === 0, 'mt-4': index > 0 }"
/>
</ul>
</template>
<script>
import { mapState } from 'vuex'
import BaseChat from '@/components/BaseChat.vue'
export default {
components: {
BaseChat,
},
created() {
this.$store.dispatch('chatQueue/fetchChats')
},
data() {
return {
currentSort: 'timeInQueue',
currentSortDir: 'desc',
chats: [],
}
},
computed: {
sortingIncomingChats() {
return this.incomingChats.slice().sort((a, b) => {
let modifier = 1
if (this.currentSortDir === 'desc') modifier = -1
if (a[this.currentSort] < b[this.currentSort])
return -1 * modifier
if (a[this.currentSort] > b[this.currentSort])
return 1 * modifier
return 0
})
},
},
}
</script>
这是具有 fetchChats 操作的 chatQueue Vuex 模块,用于将数据从 websocket 填充到 APP:
export const namespaced = true
export const state = () => ({
incomingChats: [],
error: '',
})
export const actions = {
fetchChats({ commit, rootState }) {
const data = rootState.websocket.incomingChats
commit('SET_CHATS', data)
},
}
export const mutations = {
SET_CHATS(state, data) {
state.incomingChats = data
},
SET_ERROR(state, error) {
state.incomingChats = error
console.log(error)
},
}
这是我出错的地方,因为“rootState.websocket.incomingChats”在被 fetchChats 模块操作调用时还不存在,所以我得到:
TypeError: Cannot read properties of undefined (reading 'slice')
我试图将该操作转换为异步/等待操作,但它也不起作用,但正如我提到的,我对 async/await 真的很陌生,所以也许我在这里做错了:
async fetchChats({ commit, rootState }) {
const data = await rootState.websocket.incomingChats
commit('SET_CHATS', data)
},
任何帮助将不胜感激。
万一有人遇到同样的问题,我最后做的是在我的 websocket 模块中添加一个 getter:
export const getters = {
incomingChats: (state) => {
return state.incomingChatInfo
},
}
然后在我需要用 websocket 组件填充的组件的计算值中使用 getter。
computed: {
...mapGetters('websocket', ['incomingChats']),
},
我在组件内的常规 v-for 循环中使用 getter:
<BaseChat
v-for="(chat, index) in incomingChats"
:key="index"
:chat="chat"
:class="{ 'mt-0': index === 0, 'mt-4': index > 0 }"
/>
这样我就不会遇到任何类型的 websocket 同步问题,因为我确信 getter 会在组件尝试使用它之前将数据传送到组件。
我正在尝试以尽可能模块化的方式使用来自 websocket 的数据填充我的应用程序,尝试使用最佳实践等。这很难,因为即使我已经非常深入地寻求有关使用 websockets 的建议/ Vuex 和 Vue 我仍然找不到完成这项工作的模式。来回之后,我决定使用商店来管理 websocket 的状态,然后使用该 vuex 模块填充其他组件的状态,基本上是一个聊天队列和一个聊天小部件,因此需要实时使用 websockets通讯.
这是 websocket 商店。如您所见,我正在将 processWebsocket 函数转换为承诺,以便在其他模块存储操作中使用 async/await。我看到这个工作的方式(我可能错了,所以请随时纠正我)是所有将使用 websocket 模块状态的组件将等到状态准备好然后使用它(这是目前不工作):
export const namespaced = true
export const state = {
connected: false,
error: null,
connectionId: '',
statusCode: '',
incomingChatInfo: [],
remoteMessage: [],
messageType: '',
ws: null,
}
export const actions = {
processWebsocket({ commit }) {
return new Promise((resolve) => {
const v = this
this.ws = new WebSocket('xyz')
this.ws.onopen = function (event) {
commit('SET_CONNECTION', event.type)
v.ws.send('message')
}
this.ws.onmessage = function (event) {
commit('SET_REMOTE_DATA', event)
resolve(event)
}
this.ws.onerror = function (event) {
console.log('webSocket: on error: ', event)
}
this.ws.onclose = function (event) {
console.log('webSocket: on close: ', event)
commit('SET_CONNECTION')
ws = null
setTimeout(startWebsocket, 5000)
}
})
},
}
export const mutations = {
SET_REMOTE_DATA(state, remoteData) {
const wsData = JSON.parse(remoteData.data)
if (wsData.connectionId && wsData.connectionId !== state.connectionId) {
state.connectionId = wsData.connectionId
console.log(`Retrieving Connection ID ${state.connectionId}`)
} else {
state.messageType = wsData.type
state.incomingChatInfo = wsData.documents
}
},
SET_CONNECTION(state, message) {
if (message == 'open') {
state.connected = true
} else state.connected = false
},
SET_ERROR(state, error) {
state.error = error
},
}
当我调试应用程序时,websocket 商店一切正常,我可以看到它的状态,来自服务器的数据在那里等等。当我尝试使用 websocket 填充其他组件属性时,问题就来了。当其他组件需要 websocket 状态时,这还没有准备好,所以我收到错误。这是我的一个组件尝试使用 websocket 状态的示例,我基本上从创建的循环方法中调用一个操作:
<template>
<ul class="overflow-y-auto overflow-hidden pr-2">
<BaseChat
v-for="(chat, index) in sortingIncomingChats"
:key="index"
:chat="chat"
:class="{ 'mt-0': index === 0, 'mt-4': index > 0 }"
/>
</ul>
</template>
<script>
import { mapState } from 'vuex'
import BaseChat from '@/components/BaseChat.vue'
export default {
components: {
BaseChat,
},
created() {
this.$store.dispatch('chatQueue/fetchChats')
},
data() {
return {
currentSort: 'timeInQueue',
currentSortDir: 'desc',
chats: [],
}
},
computed: {
sortingIncomingChats() {
return this.incomingChats.slice().sort((a, b) => {
let modifier = 1
if (this.currentSortDir === 'desc') modifier = -1
if (a[this.currentSort] < b[this.currentSort])
return -1 * modifier
if (a[this.currentSort] > b[this.currentSort])
return 1 * modifier
return 0
})
},
},
}
</script>
这是具有 fetchChats 操作的 chatQueue Vuex 模块,用于将数据从 websocket 填充到 APP:
export const namespaced = true
export const state = () => ({
incomingChats: [],
error: '',
})
export const actions = {
fetchChats({ commit, rootState }) {
const data = rootState.websocket.incomingChats
commit('SET_CHATS', data)
},
}
export const mutations = {
SET_CHATS(state, data) {
state.incomingChats = data
},
SET_ERROR(state, error) {
state.incomingChats = error
console.log(error)
},
}
这是我出错的地方,因为“rootState.websocket.incomingChats”在被 fetchChats 模块操作调用时还不存在,所以我得到:
TypeError: Cannot read properties of undefined (reading 'slice')
我试图将该操作转换为异步/等待操作,但它也不起作用,但正如我提到的,我对 async/await 真的很陌生,所以也许我在这里做错了:
async fetchChats({ commit, rootState }) {
const data = await rootState.websocket.incomingChats
commit('SET_CHATS', data)
},
任何帮助将不胜感激。
万一有人遇到同样的问题,我最后做的是在我的 websocket 模块中添加一个 getter:
export const getters = {
incomingChats: (state) => {
return state.incomingChatInfo
},
}
然后在我需要用 websocket 组件填充的组件的计算值中使用 getter。
computed: {
...mapGetters('websocket', ['incomingChats']),
},
我在组件内的常规 v-for 循环中使用 getter:
<BaseChat
v-for="(chat, index) in incomingChats"
:key="index"
:chat="chat"
:class="{ 'mt-0': index === 0, 'mt-4': index > 0 }"
/>
这样我就不会遇到任何类型的 websocket 同步问题,因为我确信 getter 会在组件尝试使用它之前将数据传送到组件。