vue状态管理器(vuex)
直接上示例,有不懂的可以查看vuex的官方文档:https://vuex.vuejs.org/zh/guide/state.html
一、state的用法
在src/vuex/index.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) // vuex是vue官方提供的用状态管理器,可以集中式的管理应用中所有的组件状态,更改数据的状态 // 更改vuex数据的方式;1.使用commit来触发mutations中的方法来更改数据的状态 // 2.直接更改数据中的 const state = { // state:store中存储的公共数据 baseInfo:{ // state里面的数据可以直接通过接口获取 name:'zhangsan', age:23 } } const mutations = { // mutations:用commit来触发mutations中的方法,从而触发数据更新 changeName(state,value){ state.baseInfo.name = value }, changeAge(state,value){ console.log(state) state.baseInfo.age += value } } // const commit = {} const getters = { // 通过state状态派生出其他依赖项
changeAge:function(state){ return state.baseInfo.age + 1 } } const store = new Vuex.Store({ state, mutations, getters }) export default store // 导出store状态,方便的main.js中引入,
在main.js中使用:
import Vue from 'vue' import App from './App.vue' import store from './vuex/index' Vue.config.productionTip = false new Vue({ render: h => h(App), store // 把 store 对象提供给 “store” 选项,把 store 的实例注入所有的子组件 }).$mount('#app')
在组件中使用:
<template> <div class="hello">
{{this.info.name}} <!-- 2.读取计算属性 -->
{{this.info.age}} <br> <button @click="chanAge">更改年龄</button> </div> </template> <script> export default { name: 'HelloWorld', data(){ return{ } }, computed:{ info(){ // 将store中的baseInfo数据生成为计算属性,在view中可以直接使用{{this.info.name}}
return this.$store.state.baseInfo } },
methods:{ chanAge(){ // 更改数据
this.$store.commit('changeAge',10) } } } </script>
使用mapState生成计算属性:
<template> <div class="hello"> {{this.info.name}} <!--1.读取计算属性 --> <br> {{this.infoAlias.age}} <br> {{this.infoPlusLocalState}} <br> <!-- 2.mapState传入字符串数组后可以这样使用 --> {{this.baseInfo.name +' 年龄:'+ this.baseInfo.age}} <!-- 3.使用...将计算属性合并为一个对象 --> <br> {{this.Count}} <button @click="chanAge">更改年龄</button> </div> </template> <script> import { mapState } from 'vuex' // 从vuex中引入mapState方法 export default { name: 'HelloWorld', props: { msg: String }, data(){ return{ localCount:100 } }, // 1.当组件需要获取多个状态时,一个个将其声明为计算属性会很重复和多余,可以用mapState辅助函数来帮我们生成计算属性 computed:mapState({ info:state => state.baseInfo, infoAlias: 'baseInfo', infoPlusLocalState (state) { return state.baseInfo.name + this.localCount } }), // 2.当计算属性和state中的数据同名可以直接传入一个数组 computed:mapState(['baseInfo']), // 3.当组件中还有其他计算属性时,需要将state的计算属性和其他的计算属性合并为一个对象(mapState返回的是一个对象) // 此时就可以使用扩展运算符... computed:{ Count(){ return this.localCount + 'count' }, ...mapState(['baseInfo']) }, methods:{ chanAge(){ this.$store.commit('changeAge',10) // 更改数据 } } } </script>
总结state:在view中我们使用state有3种方式:
1.直接读取store中的数据:this.$store.state.baseInfo.name
2.将state生成计算属性:this.info.name
3.使用mapState生成计算属性,减少了代码的冗余:
二、getters的用法
getters可以被认为是store的计算属性,当多个组件都需要使用这个返回值时,就不需要在每个组件中都声明该计算属性;getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
vuex/index.js 中加入getters const getters = { // 通过state状态派生出其他依赖项 otherage:function(state){ return state.baseInfo.age + 1 }, moreinfo:function(state,getter){ // 接收其他getter参与这个getter的运算 return '姓名:'+ state.baseInfo.age + '年龄:'+ getter.otherage + 1 }, whileage:(state)=> function(id){ return state.baseInfo.age + id }, }
组件中这样使用getters:
<template> <div class="hello"> <!-- 组合式api --{{count}} --> <!-- 1.getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的。所以执行chanAge方法时,getter里面的值也会发生变化 --> {{this.otherage + this.moreinfo}} <br> {{this.whileage}} <!-- 2.getters通过方法访问 --> <br> <!-- 3.使用mapGetters --> {{this.whileage(20)}}-- {{this.comAge}} <button @click="chanAge">更改年龄</button> </div> </template> <script> import { mapState,mapGetters } from 'vuex' export default { name: 'HelloWorld', data(){ return{ localCount:100 } }, // 3.当组件中还有其他计算属性时,需要将state的计算属性和其他的计算属性合并为一个对象(mapState返回的是一个对象) // 此时就可以使用扩展运算符... computed:{ Count(){ return this.localCount + 'count' }, // 1.通过属性访问 otherage(){ return this.$store.getters.otherage }, moreinfo(){ return this.$store.getters.moreinfo }, // 2.通过方法访问 whileage(){ return this.$store.getters.whileage(20) }, // 3.使用mapGetters将getter混入计算属性中 ...mapGetters([ 'otherage','moreinfo','whileage' ]), // 4.给getter属性重命名,使用对象形式 ...mapGetters({ // 把 `this.comAge` 映射为 `this.$store.getters.otherage` 'comAge':'otherage' }), ...mapState(['baseInfo']) }, methods:{ chanAge(){ this.$store.commit('changeAge',10) // 更改数据 } } } </script>
总结getters:1.通过属性访问getter:
2.通过方法访问getter:
3.通过mapGetters辅助函数:
三、Mutation的用法
改变vuex中store状态的唯一方法是提交mutation,mutations这个选项更像是事件注册,当我们需要使用mutations里面的事件时,需要调用store.commit方法来唤醒mutation handle
vuex/index.js 中修改mutations const mutations = { // mutations:用commit来触发mutations中的方法,从而触发数据更新 changeName(state,res){ state.baseInfo.name = res.name }, changeAge(state,res){ state.baseInfo.age += res.value } }
在组件中更改state的值:
<template> <div class="hello"> {{this.$store.state.baseInfo.name}} <!-- 直接读取store中的state -->
<!-- 1.直接使用commit提交 -->
<button @click="chanAge">更改年龄</button>
<br>
<!-- 2.映射为methods中的方法进行提交 -->
<button @click="changeData(obj)">更改年龄</button>
<button @click="changeName(obj)">更改名字</button>
</div>
</template>
<script>
import { mapState,mapGetters,mapMutations } from 'vuex'
export default {
name: 'HelloWorld',
data(){
return{
localCount:100,
obj:{
value:10, name:'lisi'
}
}
},
methods:{
chanAge(){
//this.$set(this.obj,'newVal',5)
// 1.提交mutation,并传参,通常以对象方式传参
//this.$store.commit('changeAge',{
// value:10
//})
this.$store.commit('changeAge',this.obj)
// 2.对象风格的提交方式(提交 mutation 的另一种方式是直接使用包含 type 属性的对象)
this.$store.commit({
type:'changeAge', value:10
})
},
// 3.使用mapMutations 将commit提交映射为methods中的方法
...mapMutations({
changeData:'changeAge'
}),
...mapMutations([
'changeName','changeAge'
])
}
}
</script>
总结mutations:提交mutation的方式
1.使用commit提交:
this.$store.commit('changeAge',this.obj)
2.以对象方式风格提交:
this.$store.commit({ type:'changeAge', value:10 })
3.使用mapMutations:
...mapMutations([
'changeName','changeAge'
])
特别注意:1.
2.mutations必须是同步函数,
官网原话:每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而,在上面的例子中 mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。
所以如果mutation函数中是一个异步请求,比如在函数中我们调用了接口,我们无法追踪到什么时候接口调用完成
四、Actions的用法:
Actions类似于mutations,不同点在于:
1.mutation是同步的,Action是异步的
2.Action提交的是mutation,而不是直接更改状态
vuex/index.js 中添加actions const mutations = { // mutations:用commit来触发mutations中的方法,从而触发数据更新 changeName(state,res){ state.baseInfo.name = res.name }, changeAge(state,res){ state.baseInfo.age += res.value }, ALERTNAME(state,res){ console.log(res) state.baseInfo.name += res.name } } const actions = { alertName(context,res){ // Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,通常会将context解构出来 // 所以我们可以直接用context.getters,context.state;但ontext 对象不是 store 实例本身。 context.commit('ALERTNAME',res) // res要传给mutation函数 }, alertName2({commit},res){ setTimeout(()=>{ commit('changeAge',res) },2000) setTimeout(()=>{ commit('ALERTNAME',res) },1000) }, }
在组件中派发action:
<template> <div class="hello"> {{this.$store.state.baseInfo.name}} <!--直接读取store中的state --> <!-- 1.分发action:通过action来提交mutation更改状态 --> <button @click="alertName">alertName</button> <br> <!-- 2.使用mapActions将组件的method映射为 this.$store.dispatch--> <button @click="alert1(obj)">alert</button> <br> <button @click="alertName(obj)">alertName1</button> </div> </template> <script> import { mapState,mapGetters,mapMutations,mapActions } from 'vuex' export default { name: 'HelloWorld', data(){ return{ localCount:100, obj:{ value:10, name:'lisi001' } } }, methods:{ alertName(){ // this.$store.dispatch('alertName',{ // name:'--action' // }) // 使用actions的原因:action内部可以进行异步操作,而mutation只能是同步函数 this.$store.dispatch('alertName2',{ name:'--action2', value:5 }) }, ...mapActions([ 'alertName', 'alertName2' ]), ...mapActions({ alert1:'alertName' }) } } </script>
组合Action
使用promise 、async/await 来组合Action,实现更多的异步操作
vuex/index.js 中添加actions function getRes(){ return { value:6, name:'await' } } const mutations = { // mutations:用commit来触发mutations中的方法,从而触发数据更新 changeName(state,res){ state.baseInfo.name = res.name }, changeAge(state,res){ state.baseInfo.age += res.value }, ALERTNAME(state,res){ state.baseInfo.name += res.name } } const actions = { alertName(context,res){ // Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,通常会将context解构出来 // 所以我们可以直接用context.getters,context.state;但ontext 对象不是 store 实例本身。 context.commit('ALERTNAME',res) // res要传给mutation函数 }, alertName2({commit},res){ setTimeout(()=>{ commit('changeAge',res) },2000) setTimeout(()=>{ commit('ALERTNAME',res) },1000) }, // 使用Promise组合action actionA({commit},res){ return new Promise((resolve,reject)=>{ if(res.value){ commit('changeAge',res) resolve() }else{ console.log('无传参!!!') reject() } }) }, actionB({dispatch,commit},res){ return dispatch('actionA',res).then(()=>{ commit('ALERTNAME',res) }) }, // 使用async/await 组合action async actionC({commit}){ commit('ALERTNAME',await getRes()) //getRes先执行 利用getRes得到res }, async actionD({dispatch,commit},res){ await dispatch('actionA',res) // 等待 actionA 完成 // await dispatch('actionA',res).then(()=>{ // console.log('actionA执行了') // }) commit('ALERTNAME',await getRes()) // 利用getRes得到res } }
在组件中使用如下:
<template> <div class="hello"> {{this.$store.state.baseInfo.name}} 直接读取store中的state <!-- 1.分发action:通过action来提交mutation更改状态 --> <button @click="alertName">alertName</button> <br> <!-- 2.使用mapActions将组件的method映射为 this.$store.dispatch--> <button @click="alert1(obj)">alert</button> <br> <button @click="alertName(obj)">alertName1</button> <!-- 组合Action -- Promise --> <button @click="actionA(obj)">actionA - Promise</button> <button @click="actionB(obj)">actionB - Promise</button> <br> <!-- 组合Action -- async/await --> <button @click="actionC">actionC - async</button> <button @click="actionD(obj)">actionD - async</button> </div> </template> <script> import { mapState,mapGetters,mapMutations,mapActions } from 'vuex' export default { name: 'HelloWorld', data(){ return{ localCount:100, obj:{ value:10, name:'lisi001' } } }, methods:{ // 组合Action -- promise actionA(obj){ this.$store.dispatch('actionA',obj).then(()=>{ console.log('age改变了') }).catch(()=>{ console.log('age没改变') }) }, actionB(obj){ console.log(this.$store.dispatch('actionB',obj)) // this.$store.dispatch('actionB',obj).then(()=>{ console.log('name改变了') }) }, // 组合Action -- async/await actionC(){ this.$store.dispatch('actionC') }, actionD(obj){ this.$store.dispatch('actionD',obj) } } } </script>
完整的vuex/index.js文件
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) // vuex是vue官方提供的用状态管理器,可以集中式的管理应用中所有的组件状态,更改数据的状态 // 更改vuex数据的方式;1.使用commit来触发mutations中的方法来更改数据状态 // 2.派发action来提交mutation更改数据状态 function getRes(){ return { value:6, name:'await' } } const state = { // state:store中存储的公共数据 baseInfo:{ // state里面的数据可以直接通过接口获取 name:'zhangsan', age:23 } } const mutations = { // mutations:用commit来触发mutations中的方法,从而触发数据更新 changeName(state,res){ state.baseInfo.name = res.name }, changeAge(state,res){ state.baseInfo.age += res.value }, ALERTNAME(state,res){ state.baseInfo.name += res.name } } // const commit = {} const getters = { // 通过state状态派生出其他依赖项 otherage:function(state){ return state.baseInfo.age + 1 }, moreinfo:function(state,getter){ // 接收其他getter参与这个getter的运算 return '姓名:'+ state.baseInfo.age + '年龄:'+ getter.otherage + 1 }, whileage:(state)=> function(id){ return state.baseInfo.age + id }, } const actions = { alertName(context,res){ // Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,通常会将context解构出来 // 所以我们可以直接用context.getters,context.state;但ontext 对象不是 store 实例本身。 context.commit('ALERTNAME',res) // res要传给mutation函数 }, alertName2({commit},res){ setTimeout(()=>{ commit('changeAge',res) },2000) setTimeout(()=>{ commit('ALERTNAME',res) },1000) }, // 使用Promise组合action actionA({commit},res){ return new Promise((resolve,reject)=>{ if(res.value){ commit('changeAge',res) resolve() }else{ console.log('无传参!!!') reject() } }) }, actionB({dispatch,commit},res){ return dispatch('actionA',res).then(()=>{ commit('ALERTNAME',res) }) }, // 使用async/await 组合action async actionC({commit}){ commit('ALERTNAME',await getRes()) //getRes先执行 利用getRes得到res }, async actionD({dispatch,commit},res){ await dispatch('actionA',res) // 等待 actionA 完成 // await dispatch('actionA',res).then(()=>{ // console.log('actionA执行了') // }) commit('ALERTNAME',await getRes()) // 利用getRes得到res } } const store = new Vuex.Store({ state, mutations, getters, actions }) export default store // 导出store状态,方便的main.js中引入,
总结Action
1.派发action :