Vuex 用于状态管理
- 状态管理模式:
-
状态:驱动应用的数据源。state
-
视图:以声明方式将状态映射到视图。
-
操作:响应在仕途上的用户输入导致的状态变化。
- Vuex 的状态存储是响应式的。不能直接改变 store 中的状态。
- 从 store 实例中读取状态最简单的方法就是在 计算属性 中返回。
this.$store.state.count
const store = createStore({
state: {
count: 0,
list:[-1, 2, 7, 0]
},
getters: {
getStr(state) {
return `这是一个数:${state.count}`
},
// 返回一个函数:过滤出大于 value 的元素
filterList: (state) => (value) => {
return state.list.filter(item => item > value)
}
},
mutations: {
increment(state) {
// 变更状态
state.count++
}
},
actions: {
increment(context) {
context.commit('increment')
}
}
})
State - mapState 辅助函数
- 当一个组件需要获取多个状态时,可以使用 mapState 辅助函数来生成计算属性:
import { mapState } from "vuex"
export default {
computed: mapState({
// 1. 箭头函数
count: state => state.count,
// 2. 传字符串参数 count 等同于 state => state.count
countAlias: 'count',
// 3. 为了能使用 this 获取局部状态,必须使用常规函数
countPlusLocalState(state) {
return state.count + this.localCount
}
})
}
-
当映射的计算属性的名称与 state 的子节点名称相同时:
computed: mapState(['count'])
-
mapState 函数返回的是一个对象。
Getter 可以认为是 store 的计算属性
-
Getter 接受 state 作为第一个参数。可以接受 其他 getter 作为第二个参数。
-
访问:
-
通过属性访问:
this.$store.getters.getStr
-
通过方法访问:
store.getters.filterList(0)
// [2, 7]
getter 在通过方法访问时,每次都会调用,不会缓存结果。
- mapGetters 辅助函数:
computed: { ...mapGetters(['count']) }
如果想将一个 getter 属性别名:
...mapGetters({ num: 'count' })
Mutation 都是同步事务
-
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation 。
this.$store.commit('increment')
-
每个 mutation 都有一个字符串的事件类型(type)和一个回调函数(handler) , 这个回调函数就是我们实际进行状态更改的地方,它接受 state 作为第一个参数。
-
调用:
-
不能直接调用一个 mutation 处理函数。需要以相应的 type 调用 store.commit 方法:
store.commit('increment')
-
提交载荷:可以向 store.commit() 传入额外的参数,即 mutation 的载荷。
store.commit('increment', { amount: 10 })
// mutation 的处理:
mutations: {
increment(state, payload) {
state.count += payload.amount
}
}
- 提交 mutation 的另一种方式是直接使用包含 type 属性的对象:
store.commit({ type:'increment', amount })
, 这是将整个对象作为载荷传给 mutation 函数,因此处理函数保持不变。
-
使用常量替代 Mutation 事件类型
-
Mutation 必须是同步函数
-
mapMutations 辅助函数,支持载荷:
// 在组件中:
methods: {
...mapMutations(['increment']) // 将 this.increment() 映射为 this.$store.commit('increment')
...mapMutations({ add: 'increment' }) // 将 this.add() 映射为 this.$store.commit('increment')
}
Action 可以包含异步操作
类似 Mutation ,不同的是:
-
Action 提交的是 mutation , 不是直接变更状态。
-
Action 可以包含任意异步操作。
-
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,可以调用 context.commit 、 context.state 和 context.getters 。
-
分发 Action :
store.dispatch('increment')
, 支持载荷方式和对象方式。 -
在组件中分发 Action :
this.$store.dispatch('increment')
或者使用mapActions
辅助函数。 -
store.dispatch() 可以处理被触发的 action 的处理函数返回的 Promise ,并且 store.dispatch 仍旧返回 Promise 。
Module 模块
-
不要在不同的、无命名空间的模块中定义两个相同的 getter 从而导致错误。
-
可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。getter、action 及 mutation都会自动根据模块注册的路径调整命名。
const store = createStore({
modules: {
user: {
namespaced: true, // 开启命名空间
getters: {
isAdmin() {} // -> getters['user/isAdmin']
}
}
}
})
- 在带命名空间的模块内访问全局内容:
-
在全局命名空间内分发 action 或提交 mutation,将
{ root: true }
作为第三参数传给 dispatch 或 commit 。 -
在带命名空间的模块注册全局 action,你可添加
root: true
,并将这个 action 的定义放在函数 handler 中。 -
带命名空间的绑定函数
-
模块动态注册 store.registerModule()
const store = createStore({/* ... */})
// 注册模块 user
store.registerModule('user', {})
// 访问
store.state.user
// 注册嵌套模块 user/info
store.registerModule(['user','info'], {}) // 注意:传的是数组,不是路径字符串
// 访问
store.state.user.info
-
动态卸载模块:
store.unregisterModule(moduleName)
注意,不能用于卸载静态模块(即创建store时声明的模块)。 -
检查模块是否已经被注册到 store :
store.hasModule(moduleName)
注意,传的是数组,不是路径字符串。
- 保留 state :
store.registerModule('a', module, { preserveState: true })
当你设置 preserveState: true 时,该模块会被注册,action、mutation 和 getter 会被添加到 store 中,但是 state 不会。
- 模块重用
如果需要创建一个模块的多个实例,如果是用纯对象来声明模块的状态,可能会造成污染问题,这时候可以使用一个函数来声明。(同 Vue 组件中的 data 都是一样的问题)
组合式API
import { useStore } from "vuex"
const store = useStore()
表单处理
如果有一种场景,需要在表单上绑定 store.state.user.name <input v-model="user.name">
且通过输入的值更新它:
-
方法1:舍弃双向绑定
<input :value="username" @input="() => {}">
,通过 @input 事件去 commit mutations 。 -
方法2:利用 带有setter 的 computed 计算属性来改变值:
computed: {
username: {
get() {
return this.$store.state.user.name
}
set(value) {
this.$store.commit('updateName', value)
}
}
}