Vuex 注入 Vue 生命周期的过程:https://mp.weixin.qq.com/s/WnRMCArDMy-PiFLprmMmCg
一、首先,想谈一下什么时候使用vuex:
(1)如果数据还有其他组件复用,建议放在vuex,比如在我所实现的项目中,实现了在线聊天功能,其中的对话框是有多个组件实现,而用户名等相关信息在各个组件中都需要使用;
(2)如果需要跨多级组件传递数据,建议放在vuex;
(3)需要持久化的数据(如登录后用户的信息),建议放在vuex,同样在我所实现的项目中,登录后用户的信息是放在了vuex中,需要使用的地方直接调用;
(4)跟当前业务组件强相关的数据,可以放在组件内;
以下文章更加详细的描述了什么场景下及为什么使用vuex:https://juejin.im/post/5b8e4b40f265da437174cbfe
vuex和浏览器缓存:https://segmentfault.com/q/1010000014557487
首先,要区别 vuex
和 浏览器缓存 的区别。
vuex
的设计是将数据存在一个对象树
的变量中,我们的应用(vue应用)从这个变量中取数据,然后供应用使用,当将当前页面关闭,vuex
中的变量会随着消失,重新打开页面的时候,需要重新生成。
而,浏览器缓存(cookie,localstorage等)是将数据存到浏览器的某个地方,关闭页面,不会自动清空这些数据,当再次打开这个页面时,还是能取到之前存在浏览器上的数据(cookie,localstorage等)。
要使用vuex
还是使用浏览器缓存,要看具体的业务场景。比如:像用户校验的token
就可以存在cookie
中,因为用户再次登录的时候能用到。而像用户的权限数据
,这些是有一定安全性考虑,且不同用户的权限不同,放在vuex
中更合理,用户退出时,自动销毁。
其次,vuex
中的 state
是单向的,也可以异步操作,这两个没有冲突。
vuex
中的state
的设计思路是保证数据的一致性和连续性,而让state
中的值只能通过action
来发起commit
,进而改变state
中的值。
而,action
中是同步
还是异步
,都是单向
地改变state
中的值。
二、接下来讲vuex是什么?
该介绍来自官网:https://vuex.vuejs.org/zh/
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
通过在根实例中注册 store
选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store
访问到。
三、从项目出发,实现在线聊天
先讲一下我写这篇文章的背景,我们有一个功能是实现聊天功能。在上一篇文章中也讲到了,我需要连接websocket,而连接websocket要求用户在登录系统的那一刻就要实现websocket的连接,这就要求我定义全局的连接,其方法比如send,onmessage等方法在需要的地方直接调用即可。
因此我采用了vuex的方式,在vuex的modules下新建了websocket.js文件,其中的代码如下所示:
import store from './user'
const state = {
websock: null
}
const mutations = {
STAFF_UPDATEWEBSOCKET (state, websock) {
state.websock = websock
}
// STAFF_SEND (state, text) {
// state.websock.send(text)
// }
}
// 实现websocket的连接,需要携带参数token
const actions = {
// 用到 ES2015 的参数解构来简化代码(特别是我们需要调用 commit 很多次的时候)
STAFF_WEBSOCKET ({ commit }) {
let token = encodeURI('Bearer ' + store.state.token)
const wsuri = 'wss://XXXXXXXXX/?Authorization=' + token + '&EIO=3&transport=websocket'
commit('STAFF_UPDATEWEBSOCKET', new WebSocket(wsuri))
// 只有定义了onopen方法,才能继续实现接收消息,即在使用的地方调用onmessage方法。
state.websock.onopen = function () {
}
// 心跳包,30s左右无数据浏览器会断开连接Heartbeat
setInterval(function () {
state.websock.send(JSON.stringify({
'heart': true
}))
}, 30000)
}
}
// 该部分为了获取websocket的相关方法。会发现此处跟mutations 里的写法是类似的,但是,想使用return,需要将相关数据写在getters里面。
const getters = {
STAFF_UPDATE (state) {
return state.websock
}
}
export default {
state,
mutations,
actions,
getters
}
相关代码注释在上述代码中已经有了体现,使用方法在下面的代码中:
1.调用websocket的send方法,即点击发送的时候,会调用send方法,将消息发送给服务端,下述代码是针对不同的定义方式,所采取的不同方法,比如,第三个方法是取得getters中的;第二个方法是取得mutations中的注释的STAFF_SEND中的方法;第一个是取得actions中定义的方法。
乍一眼看上去感觉多此一举,我们直接分发 mutation 岂不更方便?实际上并非如此,还记得 mutation 必须同步执行这个限制么?Action 就不受约束!我们可以在 action 内部执行异步操作
// Action 通过 store.dispatch 方法触发
this.$store.dispatch('STAFF_WEBSOCKET')
// this.$store.commit('STAFF_SEND').send('这是来自客户端的消息')
this.$store.getters.STAFF_UPDATE.send('sdfsfs')
Actions 支持同样的载荷方式和对象方式进行分发:
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
2.通过websocket实现接收来自服务端的消息,实现方式如下代码:
onmessage () {
let that = this
this.$store.getters.STAFF_UPDATE.onmessage = function (evt) {
let message = JSON.parse(evt.data)
that.messages.push({
content: message.content,
self: false
})
}
}
关键是从getters中获取onmessage方法,上面强调过了,在调用该方法之前需要实现onopen方法。
如上,就可以实现聊天了。