
艺术之所以存在,
就是为了使人恢复对生活的感觉,
为了使人感受事物,
使石头显出石头的质感。
--什克洛夫斯基
什么是Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
什么情况下需要用Vuex
Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
总结以下,主要是以下两点:
- 方便所有组件共享信息,方便不同组件共享信息。
- 某个组件需要修改状态和需求。
组件之间传值方式
主流的几种Vue的组件间传递信息的方式,无非有以下几种:
-
通过
$emit和props在父子组件中进行数据传递 -
用
eventbus(全局事件)在中进行传递 -
利用localstorage、sessionstorage等存储手段来进行传递
毫无疑问,这些个方式都能完成我们所要的需求,但是在某种程度上面或多或少都有一些问题。例如:
-
利用
$emit和props在兄弟关系嵌套比较深的情况之下,代码书写量将会大大的增加 -
全局事件在使用的时候可能被被多次触发、销毁时机掌握等等一系列的问题,同时作为全局事件所有页面都能监听、掌控事件可能会存在安全性问题等等...
-
...
针对以上三种传值方式存在各自的缺点,下面着重介绍以下主角Vuex的简单入门。
Vuex核心概念
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
-
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
-
你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
上面就将vuex的四个核心选项:state mutations getters actions说了出来,下面我们通过实例来了解这几个概念。
简单入门
- 第一步
新建一个.js 文件,名字位置任意,按照惯例,建议在/src/store,我们就在该文件里编辑代码。
文件位置 /src/store/index.js
// 引入vue 和 vuex
import Vue from 'vue'
import Vuex from 'vuex'
// 这里需要use一下,固定写法,记住即可
Vue.use(Vuex)
// 直接导出 一个 Store 的实例
export default new Vuex.Store({
// 类似 vue 的 data
state: {
name: 'oldName'
},
// 类似 vue 里的 mothods(同步方法)
mutations: {
updateName (state) {
state.name = 'newName'
}
}
})
这一步其实就是新建一个store,但是我们还没在项目中使用。
- 第二步
在入口文件引入上述文件, 并稍微改一下传给 new Vue()的参数,新增的行后面有备注。
文件位置 /src/main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store' //新增
Vue.config.productionTip = false
new Vue({
router,
store, //新增
render: h => h(App)
}).$mount('#app')
以上2步,如果我们使用vue-cli脚手架创建初始化项目的时候,选择了Vuex,以上两步都是默认帮我们处理好了的,使用起来很方便。
- 第三步
以上2步,其实已经完成了vuex的基本配置,接下来就是使用了。
文件位置 /src/App.vue
<template>
<div>
{{getName}}
<button @click="changeName" value="更名">更名</button>
</div>
</template>
<script>
export default {
computed:{
getName(){
return this.$store.state.name
}
},
methods:{
changeName () {
this.$store.commit('updateName')
}
}
}
</script>
这里就是一个很普通的vue文件了,有区别的地方是这里我们需要用computed属性去获取 store 里的 "data"。
还有就是我们要改变数据的话,不再用 this.xxx = xxx 改成 this.$store.commit('updateName')。
四大核心
State
首先是state,如何来获取state的值呢?一般是将这个值放置在computed里面,这样的话一旦数据发生改变的时候,就反馈到页面上面去。
computed:{
getName(){
return this.$store.state.name
}
},
state总结:用来存放组件之间共享的数据,它跟组件的data选项类似,只不过data选项是用来存放组件的私有数据。
Getter
现在假设逻辑有变,我们最终期望得到的数据(computed中的getName),是基于 this.$store.state.name 上经过复杂计算得来的,刚好这个getName要在好多个地方使用,那么我们就得复制好几份。
vuex 给我们提供了 getter来解决这个问题,可以认为是 store 的计算属性,请看代码:
文件位置 /src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// 类似 vue 的 data
state: {
name: 'oldName'
},
// 类似 vue 的 computed -----------------以下5行为新增
getters:{
getReverseName: state => {
return state.name.split('').reverse().join('')
}
},
// 类似 vue 里的 mothods(同步方法)
mutations: {
updateName (state) {
state.name = 'newName'
}
}
})
然后我们可以这样使用:
文件位置 /src/App.vue
computed:{
getName(){
return this.$store.getters.getReverseName
}
}
事实上, getter 不止单单起到封装的作用,它还跟vue的computed属性一样,会缓存结果数据,只有当依赖改变的时候,才要重新计算。
getter总结:getters主要是用来过滤和重组,这些事件最好也是能在计算属性中完成,用于监听事件变化的。
Mutation
Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type) 和 一个 回调函数 (handler),且必须是同步方法。
mutation定义:
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
mutation使用:
this.$store.commit('increment', {
amount: 10
})
mutations总结:
- 在 Vuex store 中,实际改变 状态(state) 的唯一方式是通过 提交(commit) 一个 mutation
- mutations下的函数接收state作为参数,另一个参数叫做payload(载荷),payload是用来记录开发者使用该函数的一些信息,比如说提交了什么,提交的东西是用来干什么的,包含多个字段,所以载荷一般是对象(其实这个东西跟git的commit很类似)
- 还有一点需要注意,mutations方法必须是同步方法!
Action
细心的你,一定发现我之前代码里 mutations 头上的注释了 类似 vue 里的 mothods(同步方法)。
为什么要在 methods 后面备注是同步方法呢? 因为mutation只能是同步的函数,只能是同步的函数,只能是同步的函数!!!
那么如果我们想触发一个异步的操作呢?答案是: action + $dispatch, 我们继续修改store/index.js下面的代码。
文件位置 /src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// 类似 vue 的 data
state: {
name: 'oldName'
},
// 类似 vue 的 computed
getters:{
getReverseName: state => {
return state.name.split('').reverse().join('')
}
},
// 类似 vue 里的 mothods(同步方法)
mutations: {
updateName (state) {
state.name = 'newName'
}
},
// 类似 vue 里的 mothods(异步方法) -------- 以下7行为新增
actions: {
updateNameAsync ({ commit }) {
setTimeout(() => {
commit('updateName')
}, 1000)
}
}
})
然后我们可以再我们的vue页面里面这样使用:
文件位置 /src/App.vue
methods: {
rename () {
this.$store.dispatch('updateNameAsync')
}
}
actions总结:
-
actions的作用其实和mutations是没有差别的,无非就是一个同步、异步的差别罢了。而在功能上面主要有一下两个区别:
-
actions 提交的是mutations,而不是直接变更状态。也就是说,actions会通过mutations,让mutations帮他提交数据的变更
-
actions 可以包含任意异步操作。ajax、setTimeout、setInterval不在话下
-
actions第一个参数是一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters,但是 context 对象不是 store 实例本身。下图是我从控制台打印出来的context对象
-

总结

解释一下这个图:
-
vuex 的区域是绿色虚线框的位置
-
流程:
-
每次 vue 组件需要 给 vuex 分派 一个 actions,actions 提交一个 mutation,由 mutation 来修改 state,然后再返回给 vue 组件渲染
-
state 状态只能由 mutation 来修改
-
actions 会可以封装各种 mutation 来进行修改 state
-
-
关于 state:state 就是状态
-
关于 mutation:mutation 是 vuex 对 state 或者 store提交修改的唯一方式,固定方式
-
关于 getter:
- 这里没有出现 getter, 因为 getter 在这个流程里面不需要出现,他只是一个属性,方便从 vuex 的内存里面获取一些信息,可看做state的计算属性
-
关于 actions:
-
action 提交的是 mutation,而不是直接变更状态
-
action 可以包含任意异步操作
-
下图是我总结的Vuex功能点思维导图:

参考文献: