前言:
在SPA单页面组件的开发中 Vue的vuex和React的Redux都统称为同一状态管理,个人的理解是全局状态管理更合适;简单的理解就是你在state中定义了一个数据之后,你可以在所在项目中的任何一个组件里进行获取、进行修改,并且你的修改可以得到全局的响应变更。
一、安装
`npm install vuex --save` or `yarn add vuex`
二、使用
创建文件
src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store();
export default store
main.js中引入store,全局注入store,这样就可以在每个组件里面使用this.$store
import store from './store'
new Vue({
el: "#app",
store, // 注入store
router, // 路由
templat:"<App/>",
component:{
App
}
})
store/index.js里面,我们先声明一个state变量,并赋值一个空对象给它,里面随便定义两个初始属性值;然后再在实例化的Vuex.Store里面传入一个空对象,并把刚声明的变量stater放到里面:
1. state
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state = {
//要设置的全局访问的state对象
showFooter: true,
changableNum:0
//要设置的初始属性值
};
const store = new Vuex.Store({
state
});
export default store;
这时候可以用
this.$store.state.showFooter
或this.$store.state.changebleNum
在任何一个组件里面获取showfooter
和changebleNum
定义的值,但这不是理想的获取方式;vuex官方API提供了一个getters
,和vue计算属性computed一样,来实时监听state值的变化(最新状态),并把它也放进Vuex.Store
里面。
2. getters
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state = {
//要设置的全局访问的state对象
showFooter: true,
changableNum:0
//要设置的初始属性值
};
const getters = {
//实时监听state值的变化(最新状态)
isShow(state) {
//方法名随意,主要是来承载变化的showFooter的值
return state.showFooter
},
getChangedNum(){
//方法名随意,主要是用来承载变化的changableNum的值
return state.changebleNum
}
};
const store = new Vuex.Store({
state,
getters
});
export default store;
定义的state的初始值,想要改变他的值,接下来要说的就是
mutations
,mutattions
也是一个对象,这个对象里面可以改变state的初始值的方法,具体的用法就是给里面的方法传入参数state或额外的参数,然后利用vue的双向数据驱动进行值的改变。
3. mutations
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state = {
//要设置的全局访问的state对象
showFooter: true,
changableNum:0
//要设置的初始属性值
};
const getters = {
//实时监听state值的变化(最新状态)
isShow(state) {
//承载变化的showFooter的值
return state.showFooter
},
getChangedNum(){
//承载变化的changebleNum的值
return state.changableNum
}
};
const mutations = {
show(state) {
//自定义改变state初始值的方法,这里面的参数除了state之外还可以再传额外的参数(变量或对象);
state.showFooter = true;
},
hide(state) {
//同上
state.showFooter = false;
},
newNum(state,sum){
//同上,这里面的参数除了state之外还传了需要增加的值sum
state.changableNum+=sum;
}
};
const store = new Vuex.Store({
state,
getters,
mutations
});
export default store;
通过
this.$store.commit('show')
或this.$store.commit('hide')
以及this.$store.commit('newNum',6)
在别的组件里面进行改变showfooter
和changebleNum
的值了,但这不是理想的改变值的方式;因为在 Vuex 中,mutations里面的方法都是同步事务,意思就是说:比如这里的一个this.$store.commit('newNum',sum)
方法,两个组件里用执行得到的值,每次都是一样的,这样肯定不是理想的需求
vuex官方API还提供了一个actions,这个actions也是个对象变量,最大的作用就是里面的Action方法 可以包含任意异步操作,这里面的方法是用来异步触发mutations里的方法,actions里自定义的函数接收一个context参数和要变化的形参,context与store实例具有相同的方法和属性,所以它可以执行context.commit('')
4. actions
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state = {
//要设置的全局访问的state对象
showFooter: true,
changableNum:0
//要设置的初始属性值
};
const getters = {
//实时监听state值的变化(最新状态)
isShow(state) {
//承载变化的showFooter的值
return state.showFooter
},
getChangedNum(){
//承载变化的changebleNum的值
return state.changableNum
}
};
const mutations = {
show(state) {
//自定义改变state初始值的方法,这里面的参数除了state之外还可以再传额外的参数(变量或对象);
state.showFooter = true;
},
hide(state) {
//同上
state.showFooter = false;
},
newNum(state,sum){
//同上,这里面的参数除了state之外还传了需要增加的值sum
state.changableNum+=sum;
}
};
const actions = {
hideFooter(context) {
//自定义触发mutations里函数的方法,context与store 实例具有相同方法和属性
context.commit('hide');
},
showFooter(context) {
//同上注释
context.commit('show');
},
getNewNum(context,num){
//同上注释,num为要变化的形参
context.commit('newNum',num)
}
};
const store = new Vuex.Store({
state,
getters,
mutations,
actions
});
export default store;
其他组件里进行全局执行actions里面方法的时候,你只需要用执行
this.$store.dispatch('hideFooter')
或this.$store.dispatch('showFooter')
以及this.$store.dispatch('getNewNum',6)
//6要变化的实参
// 例:
<template>
<div id="app">
<router-view/>
<FooterBar v-if="isShow" />
</div>
</template>
<script>
import FooterBar from '@/components/common/FooterBar'
import config from './config/index'
export default {
name: 'App',
components:{
FooterBar
},
data(){
return {
}
},
computed:{
isShow(){
return this.$store.getters.isShow;
}
},
watch:{
$route(to,from){
//跳转组件页面后,监听路由参数中对应的当前页面以及上一个页面
console.log(to)
if(to.name == 'book'|| to.name =='my'){
// to.name来获取当前所显示的页面,从而控制该显示或隐藏footerBar组件
this.$store.dispatch('showFooter')
// 利用派发全局state.showFooter的值来控制
}else{
this.$store.dispatch('hideFooter')
}
}
}
}
</script>
5. module
modules 模块化 以及 组件中引入 mapGetters、mapActions 和 mapStates的使用
因为在大多数的项目中,我们对于全局状态的管理并不仅仅一种情况的需求,有时有多方面的需求,比如写一个商城项目,你所用到的全局state可能是关于购物车这一块儿的也有可能是关于商品价格这一块儿的;像这样的情况我们就要考虑使用vuex中的 modules 模块化了。
// 文件可以改写
import Vue from 'vue';
import Vuex from 'vuex';
import footerStatus from './modules/footerStatus'
import collection from './modules/collection'
Vue.use(Vuex);
export default new Vuex.Store({
modules:{
footerStatus,
collection
}
});
<template>
<div id="app">
<router-view/>
<FooterBar v-if="isShow" />
<p>{{this.$store.state.footerStatus.showFooter}}</p>
</div>
</template>
<script>
import {mapState,mapGetters,mapActions} from 'vuex'; //先要引入
import FooterBar from '@/components/common/FooterBar'
import config from './config/index'
export default {
name: 'App',
components:{
FooterBar:FooterBar
},
data(){
return {
}
},
computed:{
...mapState({
//这里的...是超引用,ES6的语法,意思是state里有多少属性值我可以在这里放多少属性值
isShow:state => state.footerStatus.showFooter
//注意这些与上面的区别就是state.footerStatus,
//里面定义的showFooter是指footerStatus.js里state的showFooter
}),
},
watch:{
$route(to,from){
if(to.name=='book'||to.name=='my'){
this.$store.dispatch('footerStatus/showFooter')
// 这里改为'footerStatus/showFooter',
// 意思是指footerStatus.js里actions里的showFooter方法
}else{
this.$store.dispatch('footerStatus/hideFooter')
//同上注释
}
}
}
}
</script>
总结:
自我实践下来,如果要用好vuex,需要掌握以下的几点:
- 组件数据共享,跨页面数据共享,可以统一管理数据的存储,操作,分发。比如用户数据,比如固定的某些数据是固定某些api获取的且不止用于一个组件或者一个场景;
- 不要形式主义,每个页面都用vuex;每个模块都写getters,actions,常量方法,当你的工程量、数据量达到使用某技术场景的时候,采用某方案会觉得恰如其分;
- 处理基于数据的业务逻辑,一般是跨页面跨组件的,比如购买流程对用户余额,购物车,订单的联动影响;
- vuex核心的index做一些模块公用的存储工具,可以配置一些需要的插件或者工具类;
- 拓展:数据通讯不止vuex,简单的也可以用event bus,甚至页面内的已经能符合你的需求了;