Vuex是什么?
Vuex是一个专为Vue.js应用程序开发的状态管理模式。
它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
什么是状态管理模式
包含以下几个部分:
- state,驱动应用的数据源
- view,以声明方式将state映射到视图
- actions,响应在view上的用户输入导致的状态变化
以下是一个表示“单向数据流”理念的简单示意:
`Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。
store(仓库)
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
-
count:0 }, mutations:{ increment(state){ state.count++ } } }) ```
由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。触发变化也仅仅是在组件的 methods 中提交 mutation。
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 中的数据 }, data2 () { return this.$store.getters.xxx // 获取 getters 中的数据 } } }
-
方案三:在 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']) } } // 调用时只需 mutation1(payload) 即可触发
mapState mapGetters 是用在 computed 上的
mapMutations mapActions 是用在 methods 上的
异步修改 state 数据
mutation 里面只允许同步的去修改 state 数据。(虽然在mutation中可以异步的去修改state数据不会报错,但是会导致时间旅行等机制没有效果)
context !! 当前上下文对象,一般就理解为当前 store 的实例
SYNCSETCURCITY(context, payload) {*
*// setTimeout(() => {});*
*// },*
一般就理解为当前 store 的实例
通过它能获取 context.state 获取 state
context.getters 获取 getters
context.commit() 提交 某个mutation
context.dispath() 派发 某个action
一般 context 会使用结构赋值的方式去使用
payload 触发这个 action 时传递过来的参数
-
异步修改 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']) } } // 调用时只需 action1(payload) 即可触发
注意辅助函数的书写:mapState()、mapGetters() 书写在组件的 computed 配置项中,mapMutations()、mapActions() 书写在组件的 methods 配置项中
注意:为什么 vuex 中的 state 必须使用 mutation 来是修改:
为了以一种可以预见的方式去修改数据,不至于让数据难以理解
2. 为了实现时间旅行
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']) }
仓库模块的局部状态
仓库模块做了拆分以后,getter与muration中的第一个参数是state是当前模块的局部state。
action中的第一个参数context.context中的state也是当前模块的布局state
有时我们需要在getter中action中去获取到其余模块的state数据
getter的语法
getters:{ //state - 当前模块的state //getters- 当前模块的getters //rootState - 根模块的state数据,根据他就可以方便的去获取到其余模块的state getter1(state,getters,rootState){ } }
action 的语法
action :{ //context 是一个对象,这个对象有一些属性 //state - 当前模块的state //getters- 当前模块的getters //commit - 提交mutation的方法 //dispath - 派发action的方法 //rootState - 根模块的state数据,根据他就可以方便的去获取到其余模块的state getter1(commit,payload){ } }
!mutation没有rootState这个东西
-