VueX状态管理
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
简而言之:Vuex可在多个组件中共享状态,并且是一个 响应式 / 统一 / 状态管理 工具
安装和使用vuex
-
安装vue-vuex,也可以通过脚手架安装
npm install vuex --save
-
配置store文件
-
新建store文件夹index.js文件,导入Vue和Vuex,并且安装 Vue.use(Vuex)
-
创建Vuex.Store,配置其状态/变动/异步等
-
导出store,并在main.js中导入,挂载。
//当挂载时,内部其实在执行如下操作 Vue.prototype.$store = store
-
Vuex状态管理图
Store的配置
option:[
state:{//状态属性,又叫单一状态树},
mutations:{//变动,类似方法,用来修改state},
actions:{//异步操作},
getters:{//类似计算属性},
modules:{//划分模块}
]
state状态获取
state设置
state:{
counter:0
}
获取state属性
<div>$store.state.counter</div>
注:在js中使用记得加this
state状态改变
通过状态管理图可以看到,我们不建议直接修改$stort.state。
因为直接修改Vuex便追踪不到其状态的变化
Vuex的store状态的更新唯一方式:提交Mutation
mutations主要包括:
- 字符串的事件类型(type)//也就是函数名
- 一个回调函数(handler),该回调函数的第一个参数就是state。//函数名后面的函数
注意: mutations中的每个方法尽可能完成的事件单一一点。意思是尽量只做一件事。
actions中一般放异步操作,但如果有逻辑的操作,也可以放在actions中。
mutations改变状态方法
-
在组件中设置如点击事件,提交要运行的变动
methods:{ addCounter(){ this.$store.commit('add') } }
-
在vuex配置mutations中,设置变动方法
mutations:{ add(state){ state.counter++ //或者不传参数,直接使用this.state.counter++ } }
mutations传参技巧
-
往mutations中额外传递参数,我们称之为负载payload
-
在组件中调用方法传入参数
methods:{ changeCounter(count){ //也可以传入对象,如const count = {num:0} this.$store.commit('changeNum',count) } }
-
在mutations中传入即可
mutations:{ changeNum(state,count){ state.counter += count } }
-
mutations提交风格
-
除了普通的提交方式,我们还可以以对象的方式commit
-
把类型和参数作为payload一起传递
methods:{ changeCounter(count){ // this.$store.commit('changeNum',count) this.$store.commit({ type:'changeNum', count }) } }
-
在mutations中传入载荷并使用其属性即可
mutations:{ changeNum(state,payload){ // state.counter += count state.counter += payload.count } }
-
mutations响应风格
当属性在state中初始化时,这些属性都会被加入到响应式系统中
响应式系统会监听属性变化,当属性发生变化会通知用到属性的界面发生刷新
响应式规则:
- 提前在store中初始化所需属性
- 使用vue.set() 或Vue.delete() 做到响应式添加或删除属性
- 数组响应式方法看Vue基础学习笔记,或自行搜索
mutations常量
在mutation中, 我们定义了很多事件类型(也就是commit的方法名称).
当项目增大时,要管理的状态越来越多,有可能出现方法名称写错等情况
方法:
-
创建一个文件: mutation-types.js, 并且在其中定义我们的常量.
const ADD = 'add' -
在需要用到的地方import * as全部导出
-
在commit时正常使用,但使用mutations时作为函数名需要加一个中括号
//commit正常用 this.$store.commit(typeName.ADD) //作为函数名使用需加中括号 [typeName.ADD](){ this.state.counter++ }
mutations同步函数
通常情况下, Vuex要求我们Mutations中的方法必须是同步方法.
如果是异步操作, devtools无法记录这个操作以便调试
如果确实需要异步操作,使用actions
actions异步操作
Actions类似于Mutations, 但是是用来代替Mutation进行异步操作的.
actions传值
Actions传递的是上下文context,和store对象具有相同的方法和属性
actions分发
在Vuex状态图中看到,当有异步操作时我们要从
- vue组件分发到actions,
- 从actions提交到mutations,
- 再由mutations改变state
所以在Vue组件中, 如果我们调用action中的方法, 那么就需要使用分发dispatch
同样actions也支持传递payload
代码示例:
1. vue组件分发到actions
methods:{
changeInfo(){
this.$store.dispatch('actionInfo','payload')
}
}
2. 从actions提交到mutations
actions:{
actionInfo(context,payload){
setTimeout(()=>{
context.commit('mutationInfo',payload)
},1000)
}
}
3. 再由mutations改变state
mutationInfo(state,payload){
state.info.name = 'vicer'
console.log(payload);
}
actions返回promise
我们都知道es6中promise常用于异步操作
在Action中, 我们可以将异步操作放在一个Promise中, 并且在成功或者失败后,
调用对应的resolve或reject.
actions:{
actionInfo(context,payload){
return new Promise((resolve, reject) => {
setTimeout(()=>{
context.commit('mutationInfo',payload)
resolve()
},1000)
}).then(() => {
console.log('信息更新成功');
})
//利用promise的回调机制,这里的then除了再自身调用。
//也可以在,调用actions方法的后面使用,
//如this.$store.dispatch('actionInfo',data).then()
}
},
mapActions辅助函数
mapActions可以把store中的actions映射到局部计算属性中。
使用方法和mapGetters一样
- 导入mapActions、
import {mapActions} from 'vuex'
- 在methods中使用
- 数组方法
...mapActions(['gettersName1','gettersName2'])
- 对象方法,可自定义名字
...mapActions({gn1:'gettersName1',gn2:'gettersName2'})
- 数组方法
actions返回的promise与mapActions的配合
当使用mapActions时,可把actions的方法名传递过来进行调用,来代替this.$store.dispatch
-
导入并在methods中使用mapActions
import {mapActions} from 'vuex' methods:{ ...mapActions(['actionInfo']) }
-
替代$store中dispatch到actions的方法
//原来的方法 //this.$store.dispatch('actionInfo',data).then() //现在的方法 this.actionInfo(data).then()
getters参数和传递参数
前面已经说过,getters使用类似计算属性,我们还可以传它已经设定好的参数
getters固定参数
-
传已经设定好的状态state
getPerson(state){ //过滤函数filter,返回大于18岁 return state.persons.filter(n => n.age>18); }
-
既然getters类似计算属性,我们也可以把计算属性作为参数传进去
getLength(state,getters){ //返回length return getters.getPerson.length; }
getters传参技巧
-
我希望自己传入参数,比如我自己设置年龄并传入getters中返回
//正常思路是想办法把参数传进getters中,但它只能传state和getters设定好的属性 //解决方案是返回一个函数,利用函数来传值 getCustomize(state){ return function (nowage) { return state.persons.filter(n => n.age>nowage); } } //使用 $store.getters.arrCustomize(12)
mapGetters辅助函数
mapGetters可以把store中的getters映射到局部计算属性中。
免去了要从计算属性导入getters的麻烦
使用方法
- 导入mapGetters、
import {mapGetters} from 'vuex'
- 在computed中使用
- 数组方法
...mapGetters(['gettersName1','gettersName2'])
- 对象方法,可自定义名字
...mapGetters({gn1:'gettersName1',gn2:'gettersName2'})
- 数组方法
modules模块
Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理.
当应用变得非常复杂时,store对象就有可能变得相当臃肿.
为了解决这个问题, Vuex允许我们将store分割成模块(Modules),
而每个模块拥有自己的state、mutations、actions、getters等
获取模块中state
在Vuex中我们只是将state分割成了模块,但其实还是被放在rootState中的,
所以当我们需要获取,模块a下面的state的name时
$store.state.a.name
注意:
-
模块中的mutations、actions、getters使用和调用方法不变
-
使用模块中的actions时,只能提交模块中的mutations。
-
actions中的context保存了一些本地及根属性,可在传入时打印context查看
-
如果想在模块中使用stote中的state,只需多传一个参数rootState即可
(此参数只在模块中的getters有效)getters:{ fullName(state){ return state.name + 111 }, fullName1(state,getters,rootState){ return state.name + rootState.counter } }
项目结构
在store中,
- 我们会把state在当前页面抽离成一个单独对象引用。
- mutations,actions,getters则抽离成一个文件引用
- modules则抽离成一个单独的文件夹,文件夹中放被抽离的modules
函数思想高级使用技巧
笔记必看,链接走起
注意对比getters与mutations传参方式的区别