Vuex状态管理模式
多个组件依赖或修改同一个状态 - 共享
使用Vue.observable可以实现一个轻量型的状态管理
- 基础
- Modules
1 基础
- 基本结构与使用
- State
- Mutations
- Actions
- Gtters
- map
1.1 基本结构与使用
结构图
安装Vuex
npm install vuex --save
store.js文件
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
mutations: {
increment() {
this.state.count++
},
decrement() {
this.state.count--
}
},
state: {
count: 0
}
})
组件一
<template>
<div>
<button @click="add">+</button>
<span>{{ num }}</span>
</div>
</template>
<script>
import { store } from "../store";
export default {
store: store,
computed: {
num() {
return store.state.count;
},
},
methods: {
add() {
store.commit("increment");
},
},
};
</script>
<style>
</style>
//main.js
import { store } from './store'
new Vue({
store,
render: h => h(App),
}).$mount('#app')
//组件二
dec() {
this.$store.commit("decrement");
},
两个组件可以操作同一个值
- 在store文件中引入Vue以及Vuex
- Vue.use(Vuex),new Vuex.Store({})
- 在组件中引入store并注册,可以直接使用store
- 在根实例中引入并注册store,子组件可以通过vm.$store来访问与修改数据
1.1 State
存储状态
每个数据作为对象属性供各个组件访问
通常会使用计算属性
让状态能在组件中使用,当状态变化时会重新计算
1.2 Mutations
更改Vuex的store中状态的唯一方法是提交mutation
mutation中的回调函数接收
state
作为第一个参数mutations: { increment (state) { state.count++ } }
通过
store.commit
来声明使用哪个回调处理数据,可以传递额外的参数mutations:{ increment(state,n){ state.count +=n } } //提交 store.commit('increment',10)
参数为一个对象
mutations: { increment (state, payload) { state.count += payload.amount } } //提交 store.commit('increment',{amount:10})
对象形式的提交
//处理的回调与参数时对象的相同 mutations: { increment (state, payload) { state.count += payload.amount } } //整体提交一个对象 store.commit({type:'increment',amount:10})
如果需要添加状态,可以使用
Vue.set(obj,key,val)
,或者使用扩展符
解构赋值//提交添加状态 addOne() { store.commit("addState", { name: "张三" }); }, //添加状态的回调 addState(state, obj) { const keys = Object.keys(obj) Vue.set(state, keys[0], obj[keys[0]]) }
Mutation必须是同步函数
,异步操作时不能判定是哪个操作在改变状态
1.3 Actions
类似于mutation,不同如下:
- 提交的是
mutation
,不直接更改状态 - 可以包含
异步
操作
store
actions: {
getData(context, param) {
const oneData = [param, '数据1', '数据2']
context.commit('GETDATA', oneData)
}
},
mutations: {
GETDATA(state, data) {
Vue.set(state, 'myData', data)
},
},
state: {
myData: [],
}
组件
<template>
<div>
<button @click="getItem">请求数据</button><br />
<span v-for="(item, index) in myData" :key="index">{{ item }}</span>
</div>
</template>
<script>
export default {
computed: {
myData() {
return this.$store.state.myData;
},
},
methods: {
getItem() {
this.$store.dispatch("getData", "请求");
},
},
};
</script>
Action的回调函数接收一个与store实例具有相同属性和方法的
context
对象作为第一个参数
context !== this,context并不是store实例本身,mutation的state参数则是store实例上的真实的属性
在action中主要使用context
的commit
方法去提交mutation,所以可以使用解构赋值
的方式简化参数,可以读state中的数据actions:{ getData({commit},param){ commit('GETDATA',parm) } }
Action通过
store.dispatch
进行分发,如果回调函数返回一个Promise,可以在分发的后面进行链式操作
或者在其他action中进行链式操作actions:{ actionA({commit}){ return new Promise(async (resolve,reject)=>{ await delay(); commit('mutation1'); resolve() }) }, actionB({dispatch,commit}){ return dispatch('actionA').then(()=>{ commit('mutation2') }) } }
- Action中可以
异步
请求与操作,在不同的异步阶段提交mutation
,可以利用async/await组合action
1.4 Getters
类似与计算属性
在使用state中数据的同时,可能还需要由state派生出的一些状态,这时可以在组件内使用计算属性对state进行操作
但是当多个组件需要同样的派生状态,部分组件需要原state时,在每个组件内都使用计算属性显然很麻烦
getter可以将state中的数据计算后供组件访问
//store
state: {
user: [
{ name: '张三', age: 18 },
{ name: '李四', age: 15 },
{ name: '王二', age: 21 },
{ name: '麻子', age: 17 },
]
},
getters: {
adult({ user }) {
return user.filter(item => {
return item.age >= 18
})
}
}
//组件内
computed: {
adultUser() {
return this.$store.getters.adult;
},
},
- getters中的回调第一个参数
state
,第二个参数getters
- 可以让getter返回一个函数,这样可以向getter中传递数据,配合数组的find方法,可以对store中的数据进行
查询
1.5 map
辅助函数
- mapState
- mapGetters
- mapMutations
- mapActions
1.5.1 mapState
当一个组件需要多个
状态时,频繁使用计算属性让代码很臃肿,使用辅助函数可以简化操作
//store
state: {
a: 1,
b: 2
},
//组件
<template>
<div>
<span>{{ a }}{{ b }}</span>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: {
//数组参数
...mapState(["a", "b"]),
},
};
</script>
引入mapState
函数,参数为数组形式,但是不能改名,容易与data中的数据发生冲突
mapState函数返回一个对象
,使用展开运算符,可以逐个混入到计算属性中
//对象参数
computed: {
...mapState({
a: "a",
bar: "b",
}),
},
1.5.2 mapGetters
与mapState函数类似
- 在组件中引入mapGetters函数
- 两种传参方式
- 使用扩展运算符将getter混入
computed
对象中 - 相当于计算属性的结果在组件中使用
1.5.3 mapMutations
将组将中的methods
映射为store.commit
调用
//store
state: {
count: 0
},
mutations: {
increment(state, param) {
state.count += param
}
}
//组件
<template>
<div>
<button @click="add(10)">加10</button>
<span>{{ num }}</span>
</div>
</template>
<script>
import { mapMutations } from "vuex";
import { mapState } from "vuex";
export default {
computed: {
...mapState({
num: "count",
}),
},
methods: {
...mapMutations({
add: "increment",
}),
},
};
</script>
- 在组件中引入mapMutations函数
- 两种传递参数的方式
- 扩展运算符将返回的对象混入到
methods
中 - 相当于在methods中注册的方法,调用时可以
传递参数
,作为回调函数的第二个参数
1.5.4 mapActions
与mapMutations类似
最终可以在组件中调用,可以使用this灵活的调用
2 Module
当状态较多时,store对象就会变得越来越臃肿
Vuex支持将store对象分割成模块。每个模块拥有自己的state、mutation、action、getter甚至是嵌套子模块
2.1 模块化的导入和导出
目录结构示例
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── topics.js # 购物车模块
└── comments.js # 产品模块
//模块1
export const Topics = {
state: {
backTopic: '推荐',
userTopic: '自定义'
},
mutations: {
modify(state, param) {
state.userTopic = param
}
}
}
//模块2有同名的mutation
...
//mutathions.js 有一个同名的方法
...
//index.js
import Vue from 'vue'
import Vuex from 'vuex'
import { Topics } from './modules/topics'
import { Comments } from './modules/comments'
import { mutations } from './mutations'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
root: '根状态'
},
mutations,
modules: {
Topics,
Comments
}
})
子模块引入在modules对象中,无需改名可以使用简写形式
这样可以分模块操作,根state中会根据引入的模块混入子state对象
如果在组件中直接提交mutation,同名的mutation
会同时开始操作
在模块导出时添加
namespaced:true
,可以区分不同模块的同名操作export const Topics = { namespaced: true, state: { backTopic: '推荐', userTopic: '自定义' }, mutations: { modify(state, param) { state.userTopic = param } } }
- 模块中的action的context中可以接收到跟状态rootState
- 模块中的getter的参数:state getters rootState rootGetters
2.2 在组件中分发与提交
2.2.1 通过store直接访问
//读取state
computed: {
userTopics() {
return this.$store.state.Topics.userTopic;
},
}
//提交mutation
this.$store.commit("Topics/modify", "修改");
使用命名空间时,需要添加路径
才能提交mutation,action亦是如此
2.2.1 使用辅助函数
computed: {
...mapState({
a: (state) => state.Topics.userTopic,
}),
},
methods: {
...mapMutations({
modiT: "Topics/modify",
}),
},
对某一个模块的多次引用,可以进行简化
computed: {
...mapState("Topics", {
a: "userTopic",
b: "backTopic",
}),
},
methods: {
...mapMutations("Topics", {
modiT: "modify",
}),
},
createNamespacedHelpers
函数
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapMutations } = createNamespacedHelpers('Topics')
此时生成的辅助函数会有一个自定义的路径,可以像一般情况下使用
其他注意事项
在模块中注册全局action
将回调函数写成对象形式,添加属性
root:true
,回调写放在handler
方法中动态注册模块
创建store后使用
registerModule
函数// 注册模块 `myModule` store.registerModule('myModule', { // ... }) // 注册嵌套模块 `nested/myModule` store.registerModule(['nested', 'myModule'], { // ... })
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。