Vuex
Vuex 是什么?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化(响应式变化)。
什么是“状态管理模式”?
让我们从一个简单的 Vue 计数应用开始:
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
这个状态自管理应用包含以下几个部分:
- state,驱动应用的数据源;
- view,以声明方式将 state 映射到视图;
- actions,响应在 view 上的用户输入导致的状态变化。
以下是一个表示“单向数据流”理念的简单示意:
state的数据会在 view上显示出来,用户会根据 view 上的内容进行操作,从而触发 actions,接着再去影响 state(vue 是单向数据流的方式驱动的)。
但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
- 多个视图依赖于同一状态。
- 来自不同视图的行为需要变更同一状态。
对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!
下面的图,是把组件的共享状态抽取出来,以一个全局单例模式管理。
Vuex的应用场景
1、多个视图依赖于同一状态(读),例如token,用户信息等(需要做数据持久化处理)
2、来自不同视图的行为需要改变同一状态(写)
Vuex的安装
vue2.0只能安装vuex3.x版本,最高3.6.2;vue3.0才能装vuex4.x版本
npm install vuex@3.6.2 --save
安装 Vuex 之后,让我们来创建一个 store。
//store/index.js
// 初始化一个vuex的实例(数据仓库) 导出即可
import Vuex from 'vuex'
import Vue from 'vue'
// 使用安装
Vue.use(Vuex)
// 初始化
const store = new Vuex.Store({
// 配置(state|getter|mutations|actions)
state: {},
getters: {},
mutations: {},
actions: {}
})
export default store
//main.js
+import store from '@/store'
new Vue({
+ store,
render: h => h(App),
}).$mount('#app')
Vuex的组成
- State:数据仓库;
State本身就有数据的意思,在Vuex中代表的是数据的来源,Vuex所有数据都会存储在State中,是数据的唯一来源。 - Getter:用来获取数据;
可以认为是 store 的计算属性,就像computed计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。 - Mutation:用来修改数据;
(1)Mutation本质上也是一个function。
(2)Mutation的操作是必须是同步的,异步的话会有很大的麻烦,以下引用官方解释:
- mutation不能是异步最主要的就是为了调试,而不是不能修改状态。
- Action:用来提交mutation,可以进行异步的操作。
State
- 定义数据
// 初始化vuex对象
const store = new vuex.Store({
state: {
// 管理数据
count: 0
}
})
- 使用数据
1). 通过this直接使用
<div>A组件 state的数据:{{$store.state.count}}</div>
...
created () {
console.log(this.$store.state.count)
}
2). computed选项中使用
computed: {
count () {
return this.$store.state.count
}
}
3). mapState辅助函数使用
import { mapState } from vuex
...
computed: {
...mapState(['count'])
// 将一个 state 属性另取一个名字,使用对象形式:
// ...mapState({ newCount: 'count' })
}
...
<div>A组件 state的数据:{{count}}</div>
Getter
- 定义getters数据(理解成vuex中的计算属性)
const store = new vuex.Store({
state: {
count: 10
},
// 基于state得到一个新数据
getters: {
newCount (state) {
return state.count++
}
}
})
- 使用getters数据
1). 通过this直接使用
this.$store.getters.newCount
...
<div>{{$store.getters.newCount}}</div>
2). computed中使用
<div>{{newCount}}</div>
...
computed: {
newCount() {
return this.$store.getters.newCount
}
}
3). mapGetters辅助函数使用
import { mapGetters } from vuex
...
computed: {
...mapGetters(['newCount'])
// 将一个 getter 属性另取一个名字,使用对象形式:
// ...mapGetters({ count: 'newCount' })
},
...
<div>{{newCount}}</div>
Mutation
- 定义mutations函数
const store = new vuex.Store({
state: {
count: 0
},
mutations: {
// 修改数据的函数
add (state) {
state.count ++
},
// 带参数修改数据的函数
add2 (state, payload) {
// payload 是传参的意思
state.count += payload
}
}
})
- 使用mutations函数
1). 通过this直接使用
this.$store.commit('add')
this.$store.commit('add2', 10)
2). mapMutations辅助函数使用
import { mapMutations } from vuex
...
methods: {
...mapMutations(['add','add2'])
// ...mapMutations({ myadd: 'add' })
}
...
<button @click="add">累加1</button>
<button @click="add2(10)">累加10</button>
Action
- 定义actions函数
actions: {
// 异步获取数据
getData ({commit}, payload) {
setTimeout(()=>{
const data = 100
// 通过mutations修改数据
commit('add2', data)
},1000)
}
}
- 使用actions函数
1). 通过this直接使用
this.$store.dispatch('getData')
2). mapActions辅助函数使用
import { mapActions } from vuex
...
methods: {
...mapActions(['getData'])
// ...mapActions({ getMyData: 'getData' })
}
...
<button @click="getData">获取数据</button>
Module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter。
在使用vuex时,如果使用module后,应该如何区分每个模块呢,需要使用命名空间
// moduleA模块:
export default {
namespaced:true,
state:{
msg: '我是moduleA中的数据',
num: 0
},
getters:{
msg:state=>state.msg,
num:state=>state.num
},
mutations:{
['ADD_FUN'](state){
return state.num++;
}
},
actions:{
}
};
// moduleB模块:
export default {
namespaced:true,
state:{
msg:'我是moduleB中的msg'
},
getters:{
msg:state=>state.msg
},
mutations:{},
actions:{}
};
在store/index.js
中注册模块
import Vue from 'vue'
import Vuex from 'vuex'
import moduleA from './moduleA'
import moduleB from './moduleB'
Vue.use(Vuex)
export default new Vuex.Store({
modules:{
// 放每个模块对应的小store
moduleA,
moduleB
}
})
使用添加命名空间的module数据
import { mapGetters, mapMutations } from 'vuex'
export default {
computed: {
...mapGetters('moduleA',['msg','num']),
},
methods: {
...mapMutations('moduleB',['ADD_FUN'])
},
}
- 总结:vuex模块命名空间的使用,主要加入
namespaced:true
到各个模块中。使用时,通过如...mapGetters('模块名',['msg'])
来使用即可。
Vuex的数据持久化
vuex中的数据在页面刷新时会被初始化,所以可以引入vuex-persistedstate
进行数据持久化,其原理就是将vuex数据存储在localstorage中,不过简化了操作过程。
安装vuex-persistedstate
npm install --save vuex-persistedstate
具体使用
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from "vuex-persistedstate"
import moduleA from './moduleA'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {...},
getters: {...},
mutations: {...},
actions: {...},
modules: {...},
/* vuex数据持久化配置 */
// 默认会将vuex数据全部持久化
// plugins: [createPersistedState()]
// 按模块引入
createPersistedState({
// 存储方式:localStorage、sessionStorage、cookies
// storage: window.sessionStorage,
// 存储的 key 的key值
// key: "user",
paths: ['moduleA']
})
})
Vuex官网:https://vuex.vuejs.org/zh/installation.html
vuex-persistedstate:https://github.com/robinvdvleuten/vuex-persistedstate