vuex 状态管理
vuex是vue.js应用程序的状态管理模式。集中式存储管理应用所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
状态管理模式
new Vue({
//state: 驱动应用的数据源
data () {
return {
count: 0
}
},
//view: 以声明方式将state映射到视图
template: `<div>{{count}}</div>`,
//actions:响应在view上的用户输入导致的状态变化
methods: {
countAdd () {
return count++;
}
}
})
在小型的项目中,可以用一个简单的store模式,若用vuex反而显得繁琐。大型项目用vuex就是很好的选择了
单向数据流图示:
vuex在于解决:多个视图组件依赖同一状态,来自不同的视图的行为需要改变同一个状态,然而兄弟组件的状态传递不能实现,父子组件传参又相对繁琐。嵌套组件,状态维护困难
vuex作用:
- 提取共享状态,以全局单例模式管理
- 定义和隔离状态管理中的各种概念,并通过强制规则维持视图和状态间的独立性
- 代码结构化,易于管理和维护
vuex图示
vuex引入
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(vuex)
State —单一状态树
单一状态树(state):唯一的数据源。意味着每个应用将仅仅包含一个store实例
全局状态单例
在组件的computed计算属性中可以直接计算state,一般是直接返回store.state.count
模块化的构建中
需要在根组件中注入store选项,每个子组件就可以通过this.$store.state.count
访问状态
辅助函数 mapSate
mapState函数帮助我们当组件需要获取多个状态时,统一生成计算属性。(单个生成计算属性太繁杂)
import { mapState } from 'vuex'
export default {
data () {
return {
localCount: 0
}
}
//'''
computed: mapSate({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
因为计算属性的名称和state的子节点名称相同,ES6语法,参数也可以是字符串数组
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
])
对象展开运算符(ES6语法)
目的:将mapState返回的对象与局部计算属性混合使用,最终将对象传给computed属性
computed: {
localComputed () {
//局部计算属性的操作
return localCount++
},
...mapState([
'count'
])
}
PS:不需要把所有的状态都放入vuex,不然代码管理反而冗杂,组件自己独有的状态可以放入自己的组件内部作为局部状态
Getter —store的计算属性
Getter可以认为是store的计算属性,getter的返回值会根据它的依赖被缓存起来,且只有它的依赖值发生了变化才会被重新计算
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false },
]
},
getters: {
doneTodos: state => {
return state.todos.fliter(todo => todo.done)
}
}
})
PS:getter参数:
- 第一个参数为state对象,可接受其他getter作为第二个参数
getters: {
//...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
通过属性访问
store.getters.doneTodosCount
//或者this.$store.getters.doneTodosCount
通过方法访问
通过让getter返回一个函数,来实现给getter传参。(数组或对象查询很有用)
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2)
//{ id: 2, text: '...', done: false }
辅助函数 —mapGetters
将store中的getter映射到局部计算属性
import { mapGetters } from 'vuex'
export default {
//...
computed: {
...mapGetters([
'doneTodos' //ES6 语法,同名属性的简写,当然也可以重命名其他名字
])
}
}
Mutation —改变(类似于事件)
- 改变Vuex中的store中的state的唯一方法就是提交mutation
- 每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler),这个回调函数就是我们实际进行状态变更的地方。并且它会接受state作为第一个参数:
const store = new Vuex.Store({
state: {
count: 4
},
mutations: {
countAdd (state) {
//变更状态
state.count++
}
}
})
提交mutation
你不能直接调用一个 mutation handler
。这个选项更像是事件注册:“当触发一个类型为 countAdd
的 mutation 时,调用此函数。”要唤醒一个 mutation handler
,你需要以相应的 type 调用 store.commit
方法:
store.commit('countAdd')
提交载荷(Payload)—mutation额外的参数
- 载荷即,mutation其他参数
- 一般情况下,Payload以对象形式传参,可以包含多个字段并记录Mutation
- 在提交时可以是对象乡试提交
mutations: {
countAdd: (state,Payload) {
state.count += Payload.acount
}
}
提交方式:
//一般提交方式,只当第二个参数为载荷Payload
store.commit('countAdd',{
acount: 10
})
//对象提交方式,把type也当作载荷Payload
store.commit({
type: 'countAdd',
acount: 10
})
在组件中提交mutation
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'countAdd', // 将 `this.countAdd()` 映射为 `this.$store.commit('countAdd')`
]),
...mapMutations({
add: 'countAdd' // 将 `this.add()` 映射为 `this.$store.commit('countAdd')`
})
}
}
mutation必须是同步函数
原因很简单,我们的mutations是改变状态的,那么状态一改变就应当更新相应组件。而异步函数中状态的改变是不可追踪的,我们不知道什么时候状态发生了变化。所以mutations中不允许异步函数。为了解决这一问题,出现了action
Action
- Action提交的是mutation,而不是直接变更状态
- Action可以包含任意异步操作
- Action接受一个与store实例具有相同方法和属性的context对象。可以调用
context.commit
提交一个mutation
,和通过context.state
和context.getters
来获取state
和getters
- Action通过store.dispatch方法分发
- Action和mutations一样支持载荷和对对象形式分发
在组件中分发Action
import { mapAction } from 'vuex'
export default {
// ...
methods: {
...mapAction([
'countAdd', // 将 `this.countAdd()` 映射为 `this.$store.dispatch('countAdd')`
]),
...mapAction({
add: 'countAdd' // 将 `this.add()` 映射为 `this.$store.dispatch('countAdd')`
})
}
}
组合Action —用到promise和async/await实现
- store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise
- store.dispatch 仍旧返回 Promise
- 利用async/await实现Action组合
// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
Module
Veux允许我们将store分割成模块(module)。每个模块(module)拥有自己的state,mutation,action,getter。甚至嵌套子模块
const moduleA = {
state: { ... },
//参数增加
//参数(state,Payload)
mutations: { ... },
//参数({state,context,rootState})
actions: { ... },
//参数(state,getters,rootState)
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
state: { ... },
getters: { ... },
actions: { ... },
mutations: { ... }
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
PS:模块这一节涉及到的东西较多,参考官网Vuex模块(modules)