vuex 介绍
vuex 是什么
- vue 官方提供的状态管理器,用于处理组件的数据共享问题
什么时候需要使用 vuex
- 当多个组件需要共享状态的时候,使用 vuex 能更好的帮助我们管理这些需要共享的状态
vuex 仓库(store)的五大核心
- state :专门存储共享数据的地方
- getters : 针对现有 state 数据或者其余的 getter 数据做二次计算的数据,可以理解为仓库的计算属性
- mutations : 唯一能够修改 state 数据的地方,并且只能同步修改。
- actions : 这里面可以写异步代码,如果如要修改 state 中的数据,必须通过调用 mutation 来修改
- modules : 仓库模块的拆分
vuex 仓库中的四个辅助函数
- mapState() 获取 state 中的数据
- mapGetters() 获取 getters 中的数据
- mapMutations() 获取 mutations 配置项中同步修改 state 中数据的方法
- mapActions() 获取 actions 配置项中异步修改 state 中数据的方法
使用 vuex
-
在项目中安装 vuex
$ npm install vuex
-
创建 src/store.js 文件,用来生成仓库的实例,并且生成的实例需要在 main.js 文件 new Vue() 中挂载
以下代码在 store.js 文件
// 引入 vue import Vue from 'vue' // 引入 vuex import Vuex from 'vuex' Vue.use(Vuex) // 创建仓库实例 const store = new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {}, modules: {} }) // 暴露 store export default store
组件中使用 state 与 getter
-
方案一:使用挂载到 Vue 原型上的 $store 对象,这个 $store 就是 new Vue.Store() 生成的仓库实例对象
// 直接在页面上使用时 $store.state.xxx || $store.getters.xxx
-
方案二:在 computed 中使用
export default { computed: { data1 () { return this.$store.state.xxx // 获取 state 中 xxx 数据 }, data2 () { return this.$store.getters.xxx // 获取 getters 中 xxx 数据 } } }
-
方案三:在 computed 中使用 辅助函数,辅助函数内部就是用上述方案二展开实现的
// 使用 辅助函数必需先引入 import { mapState, mapGetters } from 'vuex' export default { computed: { // mapState 接收一个数组做为参数,参数中的每一项,就是在仓库中的 state 数据 ...mapState(['state', 'state2']) // mapGetters 接收一个数组做为参数,参数中的每一项,就是在仓库中的 getter 数据 ...mapGetters(['getter1', 'getter2']) } } // 如果希望组件中数据与仓库中数据不同名,采用第二种方案即可
组件中修改 state 与 getter
注意:state 可以被修改,getter 不允许修改
-
同步修改 state ,需要仓库中提供对应修改 state 的 mutation 函数
-
方案一:使用 vuex 挂载到 vue 原型上的 $store 对象的 commit() 方法
// 直接在页面上使用时 $store.commit('updateNmae', payload)
-
方案二:在 methods 中使用
export default { methods: { fn (payload) { // updateNmae 是仓库中 mutations 配置相中定义的函数名称,payload 传递过去的参数 this.$store.commit('updateName', payload) } } }
-
方案三:在 methods 中使用 辅助函数,辅助函数内部就是用上述方案二展开实现的
// 使用 辅助函数必需先引入 import { mapMutations } from 'vuex' export default { methods: { // mapMutations 接收一个数组做为参数,参数中的每一项,就是在仓库中的 mutation 方法 ...mapMutations(['mutation1', 'mutation2']) } } // 调用时只需 this.mutation1(payload) 即可触发
异步修改 state 数据
mutation 里面只允许同步的去修改 state 数据。(虽然在mutation中可以异步的去修改state数据不会报错,但是会导致时间旅行等机制没有效果)
-
异步修改 state ,需要仓库中提供对应修改 state 的 action 函数
-
方案一:使用 vuex 挂载到 vue 原型上的 $store 对象的 dispatch() 方法
// 直接在页面上使用时 $store.dispatch('actionName', payload)
-
方案二:在 methods 中使用
export default { fn (payload) { setTimeOut(() => { this.$store.dispatch('actionName', payload) }, 1000) } }
-
方案三: 在 methods 中使用 辅助函数,辅助函数内部就是用上述方案二展开实现的
// 使用 辅助函数必须先引入 import { mapActions } from 'vuex' export default { methods: { // mapActions 接收一个数组做为参数,参数中的每一项,就是在仓库中的 action 方法 ...mapActions(['action1', 'action2']) } } // 调用时只需 this.action1(payload) 即可触发
注意辅助函数的书写:mapState()、mapGetters() 书写在组件的 computed 配置项中,mapMutations()、mapActions() 书写在组件的 methods 配置项中
注意:为什么 vuex 中的 state 必须使用 mutation 来是修改:
为了以一种可以预见的方式去修改数据,不至于让数据难以理解
为了实现时间旅行
vuex 中的 module
-
什么时候需要在 vuex 中使用 module
- 项目越做越大,功能点越写越多。需要使用 vuex 共享的数据越来越庞大时,就需要使用 module 来进行仓库模块拆分,拆分的每个模块都拥有自己的 state、mutation、action、getter,甚至是嵌套子模块
// 拆分的仓库 子模块A const moduleA = { state: { }, mutations: { }, actions: { }, getters: { } } // 拆分的仓库 子模块B const moduleB = { state: { }, mutations: { }, actions: { } } // 仓库根模块 const store = new Vuex.Store({ // 通过 modules 配置选项将拆分出去的子模块配置到根仓库中 modules: { aa: moduleA, bb: moduleB } }) store.state.aa // -> moduleA 的状态 store.state.bb // -> moduleB 的状态
-
仓库拆分子模块后,没有设置命名空间引起的问题
默认情况下,模块内部的 getter、mutation 、action是注册在全局命名空间的
- 多个子模块中的 getter 不能同名,否则会报错
- 多个子模块中的 mutation 如果同名的话,组件调用这个 mutation 时,都会被触发,会引起全局污染
- 多个子模块中的 action 如果同名的话,组件调用这个 action 时,都会被触发,会引起全局污染
-
由于上述问题,需要给每个子模块设置命名空间
-
给每个子模块的对象配置中添加一个 namespaced 属性即可,属性值为 true
// 拆分的仓库 子模块A const moduleA = { namespaced: true, // 给子模块设置命名空间 state: { }, mutations: { }, actions: { }, getters: { } }
-
设置后,子模块中的 getter、mutation、action 是注册在自己命名空间中
-
-
设置了命名空间后,使用的时候需要加上仓库的名称
-
获取某个仓库子模块中的 state
// 1.直接页面上获取时 $store.state.xxx.stateKey // xxx 代表仓库子模块的名称 // 2.使用计算属性 computed: { data () { return this.$store.state.xxx.stateKey } } // 3.使用辅助函数 mapState() computed: { ...mapState('xxx', ['state1', 'state2']) // 原理:使用上述计算属性展开的 } // 注意:如果在组件中同时拿多个仓库子模块的同名 state 数据,应使用上面计算属性的方式
-
获取某个仓库子模块中的 getter
// 1.页面上直接使用 $store.getters['xxx/getterKey'] // 2.使用计算属性 computed: { data () { return this.$store.getters['xxx/getterKey'] } } // 3.使用辅助函数 mapGetters() computed: { ...mapGetters('xxx', ['getter1', 'getter2']) }
-
提交摸个仓库子模块中的 mutation
// 1.页面上直接使用 $store.commit('xxx/mutationKey', payload) // 2.自定义函数中使用 methods: { fn (payload) { this.$store.commit('xxx/mutationKey', payload) } } // 3.使用辅助函数 mapMutations() methods: { ...mapMutations('xxx', ['mutation1', 'mutation2']) }
-
派发某个仓库子模块中的 action
// 1.页面上直接使用 $store.dispatch('xxx/actionKey', payload) // 2.自定义函数中使用 methods: { fn (payload) { this.$store.dispatch('xxx/actionKey', payload) } } // 3.使用辅助函数 mapActions() methods: { ...mapActions('xxx', ['action1', 'action2']) // OR ...mapActions({ xxx: 'xxx/action1', yyy: 'xxx/action2' }) }
-
-
仓库拆分子模块后,各子模块间的状态获取 (只能在 getter 与 action 中获取)
-
在 getter 中获取,根节点状态会作为第三个参数暴露出来
const moudelA = { getters: { getter1 (state, payload, rootState) { // rootState 就是根节点状态 console.log(rootState) // 只能读取 } } }
-
在 action 中获取,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState
const moduleA = { actions: { action1 ({ state, commit, rootState }) { console.log(rootState) // 只能读取 } } }
-
-
v-model 指令与 store 仓库数据双向绑定
-
不用指令时
<input :value="message" @input="updateMessage"> computed: { ...mapState(['message']) }, methods: { updateMessage (event) { this.$store.commit('updateMessage', event.target.value) } }
-
使用 v-model 指令时
<input v-model="message"> computed: { message: { get () { return this.$store.state.message }, set (value) { this.$store.commit('updateMessage', value) } } }
-
-
学习更多:vuex 官网