前言
我们知道,父组件通过prop向子组件传递数据,子组件通过定义事件向父组件传递数据。但假若遇到多个组件访问统一数据时,就需要一个全局的状态管理方案vuex来解决。
Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储来管理应用程序中所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex也集成到了调式工具vue-devTools中,提供了一些高级调试功能。
一、安装Vuex
使用NPM安装方式,在命令行中输入:
npm install vuex --save
在Vue脚手架项目中使用,在main.js文件中导入Vuex,并使用Vue.use()来安装插件。
//main.js
import Vue from 'vue'
import axios from 'vuex'
Vue.use(Vuex) //安装插件
二、核心概念
1.store
每一个Vuex应用的核心就是store,它可以理解为保持应用程序状态的容器。store与普通的全局对象区别有以下两点:
1.Vuex的状态存储是响应式的。当Vue组件从store中检索状态时,如果store的状态发生变化,那么组件也会得到及时更新。
2.不能直接改变store中的状态。改变store中的状态的唯一途径是显示地提交mutation。
在创建store前先了解下vuex的工作原理。
我们可以将虚线框理解成是store,可以看到store里面包含了Actions、Mutations、State三个函数,每个函数都有不同作用。
Actions可以实现异步或批量操作,Mutation中放的是要对state中数据修改的内容,State是用来存储全局公用的数据。若要改变组件数据,必须通过Mutations(可以略过Actions)。
由图可知,可以通过Commit方法来调用Mutations,通过Mutate方法来调用State,通过Dispatch方法来调用Actions。
了解了vuex的工作原理后,我们开始创建store。
1.首先在src下创建store/index.js
在文件中new一个store实例
export default new Vuex.Store({
state:{
city:'zhuhai'//这里存放的是全局数据city
}
});
若要在组件中使用city这个数据,用this.$store.state.city调用
<div class="">{{this.$store.state.city}}</div>
2.在main.js文件中引入store,并在创建根vue实例的时候将store传入进去
//main.js
import store from "./store(store的路径)"
new Vue({
router,
store,//在此添加
})
2.mutations
mutations是当我们需要修改state中的数据时必须用到的,而在没有异步/批量操作时actions可用可不用
1.首先在组件中用Dispatch()来调用Actions
methods:{
handleCityClick(city){
this.$store.dispatch('changeCity',city)//派发一个名为changeCity的actions对象
}
}
2.在store/index.js里写一个actions对象
export default new Vuex.Store({
state:{
city:'zhuhai'//这里存放的是全局数据city
},
actions: {
//这里的city就是从组件中dispatch传过来的,ctx为上下文对象
changeCity(ctx, city) {
ctx.commit("changeCity", city);//用commit来调用Mutations
},
Mutations:{
changeCity (state,city){
state.city=city//将新传过来的city赋值给state.city
}
}
},
modules: {}
});
如果不用actions,则直接在组件方法中调用this.$store.commit()即可
3.mapState
当一个组件需要使用多个store状态属性时,将这些状态都声明为计算属性就会显得重复和冗余。为了解决这个问题,可以使用mapState辅助函数帮助我们生成计算属性。
import { mapState } from 'Vuex'
export default {
//...
computed:{
//1.单个数据
//...mapState(['city'])
//2.数组写法
...mapState({
currentcity:'city'
})
}
}
通过mapState,把公用数据映射到city后,调用数据的写法从this.$store.state.city变为this.city。
同理,mapMutations也是辅助函数简化mutation的提交,用法和mapState一样。
4.getter
getter类似computed,当需要根据state中的数据算出新数据时,借助getter来提供新数据。
假如在store的状态中定义了一个图书数组,代码如下:
export default new Vuex.Store({
state:{
books:[
{id:1,title:'vuex getter',isSold:false},
{id:2,title:'book',isSold:true}
]
},
...
})
在组件内需要得到正在销售的图书,于是定义一个计算属性sellingBooks,对state中的books进行过滤。代码如下:
computed:{
sellingBooks(){
return this.$store.state.book.filter(book=>book.isSold === true);
}
}
如果多个组件都需要用到sellingBooks属性,无论是复制代码还是抽取为共享函数导入都不理想。这时我们可以在store中定义getters,它的返回值会根据它的依赖项被缓存起来,且只有在它的依赖项发生改变时才会重新计算。
getter接受state作为其第一个参数,代码如下:
export default new Vuex.Store({
state:{
books:[
{id:1,title:'vuex getter',isSold:false},
{id:2,title:'book',isSold:true}
]
},
getters:{
sellingBooks:state => state.books.filter(book => book.isSold == true)
}
})
我们定义的getter将作为store.getters对象的属性来访问,代码如下:
<ul>
<li v-for="book in this.$store.getters.sellingBooks" :key="book.id">
{{book.title}}
</li>
</ul>
getter也可以接收其他getter作为第二参数。
getters:{
sellingBooks:state => state.books.filter(book => book.isSold == true),
sellingBooksCount:(state,getters)=>{
return getters.sellingBooks.length
}
}
如果要简化上述getter计算属性的访问方式(this.$store.getters.sellingBooks),可以使用mapGetters,用法同mapState类似。
computed:{
//传递数组作为参数
...mapGetters([
'sellingBooks',
'sellingBooksCount',
...
]),
//传递对象作为参数
..mapGetters({
booksCount:'sellingBooksCount'
})
}
5.module
当应用程序复杂时,store对象就会非常臃肿,为了方便管理,将store划分为多个模块,每个模块包含自己的state、mutations、actions、getters,以及嵌套的子模块。
const moduleA = {
state:{...},
mutations:{...},
actions:{...},
getters:{...}
}
const moduleB = {
state:{...},
mutations:{...},
actions:{...},
getters:{...}
}
...
const store = new Vuex.Store({
modules:{
a:modulesA,
b:modulesB
}
})
获取状态:
store.state.a
store.state.b
模块内部状态
const moduleA = {
state:{count:0},
mutations:{increment(state){
state.count++//state对象是局部模块状态
}
},
getters:{
doubleCount (state) {
return state.count*2
}
}
}
总结
本文介绍了Vuex的使用,要注意的是,不要直接去修改store中的状态,而应该通过mutations来修改,在组件内,mutation是映射为组件的方法来使用的,而store中的state和getter是映射为计算属性来使用的,store中的action也是映射为组件的方法来使用的。如果应用比较复杂,可以使用module模块化管理,同时访问也会变得复杂。