vuex是一个依赖于vue的状态管理工具,在项目中也不是必须的,但当项目足够大,一般都会用到,因为它的确解决了不少问题:
- 父子组件可以通过
props来传递参数,而兄弟组件呢?在遵循单向数据流的设计原则下,vuex可以实现 - 可以作为一个全局响应式变量,如果定义一个全局变量,不仅可能造成污染,也无法实现响应式,这种情况下vuex就特别合适了
vuex的核心是store,里面储存的都是响应式属性,并且不能直接修改store中的状态,必须通过显示的提交(commit)对应的mutation来完成,只有这一种方式。
辅助函数
state
在项目中使用state:
const counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count // count状态
}
}
}
如果当某个组件需要用到多个state,使用枚举的方式那就需要在computed中罗列出多个,这样做可以,但看起来很不优雅和冗余,这个时候就可以使用辅助函数mapState
computed: mapState([
'count' // 映射this.count为this.store.state.count
])
getter
getter相当于store的计算属性,也就是说在业务中,状态可能不是我们直接需要的对象,而需要额外的处理,避免每次都去处理相同的逻辑,就用getter来封装:
computed: {
// 我们可能仅需要任务已完成的数量
doneTodosCount () {
return this.$store.state.todos.filter(todo => todo.done).length
}
}
对于其辅助函数,和mapState的意义相同,也是避免冗余的属性重复编写:
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
...mapGetters([
'doneTodosCount', // 把this.doneTodosCount映射为this.$store.getters.doneTodosCount
// ...
])
}
}
源码实现
在vuex代码src/index.js路径下导出了辅助函数方法:
接着往下看helpers.js文件:
我们以mapState方法看看,其实代码很简单,这个函数的主要作用就是把多个state组成的mapState拆分成一个对象,里面包含了所有定义的state
export const mapState = normalizeNamespace((namespace, states) => {
const res = {}
// mapState必须是数组或对象,否则会报错
if (__DEV__ && !isValidMap(states)) {
console.error('[vuex] mapState: mapper parameter must be either an Array or an Object')
}
normalizeMap(states).forEach(({ key, val }) => {
res[key] = function mappedState () {
let state = this.$store.state
let getters = this.$store.getters
// 处理modules
if (namespace) {
const module = getModuleByNamespace(this.$store, 'mapState', namespace)
if (!module) {
return
}
state = module.context.state
getters = module.context.getters
}
return typeof val === 'function'
? val.call(this, state, getters)
: state[val]
}
// mark vuex getter for devtools
res[key].vuex = true
})
return res
})