目录
2.4 创建vuex的store实例并注册上面引入的各大模块
这种方式需要另外创建一个vue实例,用来当做消息总线。见vuepro02-bus示例。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。可以想象为一个“前端数据库”(数据仓库), 让其在各个页面上实现数据的共享包括状态,并且可操作。
Vuex分成五个部分: 1.State:单一状态树 2.Getters:状态获取 3.Mutations:触发同步事件 4.Actions:提交mutation,可以包含异步操作 5.Module:将vuex进行分模块
Vuex是专门为vue应用程序开发的状态管理模式,将组件的共享状态抽取出来,以一个全局单例模式进行管理,组件树构成一个巨大的视图,不管组件在树的何种位置,任何组件都能获取到状态和触发行为。可以将其想象为一个“前端数据库”(数据仓库),让其在各个页面上实现数据的共享包括状态,并且可操作。(核心就是 解决组件间的通讯问题)
Vuex分成五个部分: 1.State:单一状态树 2.Getters:状态获取 3.Mutations:触发同步事件 4.Actions:提交mutation,可以包含异步操作 5.Module:将vuex进行分模块
npm install vuex -S
main.js是vue应用程序的入口,在这个文件中导入vuex组件。
通过在根实例中注册store选项,该store实例会注入到根组件下的所有子组件中,且子组件可以通过this.$store访问到。
state.js的作用可以看作是存放全局参数的容器,组件可以通过state.js获取全局参数。
//存放全局参数的容器,组件可以通过state.js获取全局参数
const state = {
LeftAsideState: 'open'
}
export default state
当在TopNav.vue中点击展开或折叠时,需要将当前的状态设置到全局参数中,以便于其他组件可以获取到状态。 mutations:相当于setter方法,处理数据的唯一途径,state的改变或赋值只能在这里。
🤔🤔1) mutations.js
//Mutation 必须是同步函数。原因:异步方法,我们不知道什么时候状态会发生改变,所以也就无法追踪了
//如果我们需要异步操作,Mutations就不能满足我们需求了,这时候我们就需要Actions了
const mutations = {
//state,即state.js中定义的state,借此可以访问state中定义的全局变量
//payload: 载荷,保存传递参数的容器
setLeftAsideState: (state, payload) => {
//通过载荷为全局参数赋值,相当于setter
state.LeftAsideState = payload.LeftAsideState;
}
}
export default mutations
🤔🤔2)如何调用mutations.js中定义的setLeftAsideState为全局参数赋值? 见一下示例: 当点击TopNav.vue组件中的折叠或展开按键时,需要将当前的状态设置到全局参数中,以便于其他组件可以获取到状态。 TopNav.vue
//转换折叠图标的状态
doToggle: function() {
//如果原来为打开状态则点击后转换为折叠
//如果原来为折叠状态则点击后转换为打开
this.opened = !this.opened;
//通过自定义事件将状态值传递个父组件,及Main.vue
//相应的Main.vue组件中需要设置‘left-open-collapsed’
//使用vuex方式后,将原来的实现方式注释
//this.$emit("left-open-collapsed",this.opened);
/*
* 通过vuex进行组件间的通讯,当点击折叠或展开时设置全局参数,以便于
* 其他组件获取状态。
*
* 第一种提交方式:
* this.$store.commit(type, payload);
* 参数type: 与mutations.js定义的方法名一致
* 参数payload:载荷,是个json对象,其中的参数与state.js中定义的全局参数名一致
* 该方法的作用是为全局参数LeftAsideState赋值。
*
* 第二种提交方式:
* this.$store.commit({type: 'setLeftAsideState', LeftAsideState: 'open'});
* 即:将type参数与要设置的全局参数放在一个json对象中。
*/
this.$store.commit("setLeftAsideState", {
LeftAsideState : this.opened ? 'open' : 'collapsed'
});
}
Main.vue组件获取设置的全局变量(LeftAsideState)的值,并需要根据变量的值来改变自身的状态。 state中存放的状态值是响应式的,从store实例中读取状态最简单的方式是在计算属性中返回某个状态。
/*
* 通过计算属性读取store中的值,并根据获取到的值返回展开或折叠样式。
* 定义好计算属性后,通过绑定的方式使用
* <el-aside :>
*/
computed: {
leftAsideClass: function() {
//可以通过以下的方式进行获取,但是不推荐。
let tmp = this.$store.state.LeftAsideState;
return tmp == 'open' ? "main-aside" : "main-aside-collapsed";
}
}
<template>
<el-container class="main-container">
<!-- 侧边栏有折叠效果,通过class控制折叠还是显示的效果 -->
<!--leftAsideClass 为定义的计算属性 -->
<el-aside :class="leftAsideClass">
<!-- 原来使用父子组件传参方式,修改为vuex方式实现 -->
<!-- <LeftAside :opened="opened"></LeftAside> -->
<LeftAside></LeftAside>
</el-aside>
<el-container>
<el-header class="main-header">
<!-- 原来使用父子组件传参方式,修改为vuex方式实现 -->
<!-- <TopNav @left-open-collapsed="toggleLeftStat"></TopNav> -->
<TopNav></TopNav>
</el-header>
<el-main class="main-center">
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
//getters将state中定义的值暴露在this.$store.getters对象中
//可以通过如下代码访问:this.$store.getters.getLeftAsideState
const getters = {
//参数state即为stroe中存放的state,在state.js定义
getLeftAsideState: function(state) {
return state.LeftAsideState;
}
}
export default getters
/*
* 通过计算属性读取store中的值,并根据获取到的值返回展开或折叠样式。
*/
computed: {
leftAsideClass: function() {
//可以通过以下的方式进行获取,但是不推荐。
//let tmp = this.$store.state.LeftAsideState;
//getters方式获取状态值
let tmp = this.$store.getters.getLeftAsideState;
return tmp == 'open' ? "main-aside" : "main-aside-collapsed";
}
}
computed: {
//通过计算属性获取定义在store中的状态值,并根据状态值来设定返回值,用于标记左侧栏展开或折叠的状态
//因为命名冲突,可将上面再data中定义的同名属性删除.
isCollapse: function() {
return this.$store.getters.getLeftAsideState == 'open' ? false : true;
}
}
同步 同步模式,即上述所说的单线程模式,一次只能执行一个任务,函数调用后需等到函数执行结束,返回执行的结果,才能进行下一个任务。如果这个任务执行的时间较长,就会导致「线程阻塞」
var x = true; while(x); console.log("don't carry out"); //不会执行
setTimeout(function(){
console.log("async task .... ");
}, 3000);
现做一个异步修改参数值的简单示例:
🤔🤔1) 在store中声明一个参数
//存放全局参数的容器,组件可以通过state.js获取全局参数
const state = {
LeftAsideState: 'open',
//声明一个存放人员名称的参数,设置默认值,用于演示异步修改参数
PersonName:'张飞'
}
export default state
🤔🤔2) 在mutations.js中定义修改参数的方法:
//Mutation 必须是同步函数。为什么呢?异步方法,我们不知道什么时候状态会发生改变,所以也就无法追踪了
//如果我们需要异步操作,Mutations就不能满足我们需求了,这时候我们就需要Actions了
const mutations = {
//state,即state.js中定义的state,借此可以访问state中定义的全局变量
//payload: 载荷,保存传递参数的容器
setLeftAsideState: (state, payload) => {
//通过载荷为全局参数赋值,相当于setter
state.LeftAsideState = payload.LeftAsideState;
},
//设置人员名称,用于演示异步
setPersonName: (state, payload) => {
state.PersonName = payload.PersonName;
}
}
export default mutations
//getters将state中定义的值暴露在this.$store.getters对象中
//可以通过如下代码访问:this.$store.getters.getLeftAsideState
const getters = {
//参数state即为stroe中存放的state,在state.js定义
getLeftAsideState: function(state) {
return state.LeftAsideState;
},
//定义获取人员名称的方法
getPersonName: function(state) {
return state.PersonName;
}
}
export default getters
/*
* Action与mutation定义语法类型,不同点:
* 1) Action提交的是mutation,而不是直接变更状态,mutation直接变更状态
* 2) Action可以包含任意异步操作
* 3) Action的回调函数接收一个 context 上下文参数
* 详见方法内的注释
*/
const actions = {
/*
* context为上下文参数,与 store 实例有着相同的方法和属性
* context参数包含:state、rootState、getters、mutations、actions 五个属性
* payload为负载,是一个存放需要传递的参数的容器,和mutations的含义一致
*/
setPersonNameAsyn: function(context, payload) {
//异步操作,第一个参数为一个匿名函数,第二参数为延迟的时间,单位为毫秒。
//作用:在3秒或调用第一个参数定义的匿名函数。
setTimeout(function() {
//action提交的是mutation,而不能直接变更状态
//第一个参数setPersonName即为mutation中定义的方法名
//payload即为参数容器。
//通过context的commit方法提交一个mutation,由mutation负责
//修改参数,action负责提供异步功能,(mutation必须是同步的)
context.commit('setPersonName', payload);
}, 3000);
}
}
export default actions
<template>
<div>
<h1>首页</h1>
<p>
{{personName}}
</p>
<p>
<button @click="setPersonName">异步设置人员名称</button>
</p>
</div>
</template>
<script>
export default {
name: 'Home',
data: function() {
return {
}
},
computed: {
personName: function() {
return this.$store.getters.getPersonName;
}
},
methods: {
setPersonName: function() {
//调用定义在actions中的方法,调用方式如下:
//this.$store.dispatch(type,payload);
//第一个参数type: 定义在actions中的方法名
//第二个参数payload:负载(一个json对象),即要传递的参数
this.$store.dispatch('setPersonNameAsyn', {
PersonName: '关羽'
});
}
}
}
</script>
<style>
</style>
//VUEX 异步请求后台数据
'VUE_ASYN_REQ':'/userMsg/vueAsynAction!asynAction.action',
const actions = {
......
/*
* 在actions.js中无法直接获取vue实例的this对象,但需要该对象来获取axios
* 对象,以便于发送ajax请求,可以通过payload参数由调用者传入vue实例的this
* 对象。
*/
getDataFromApiAsyn: function(context, payload) {
let _this = payload._this;
let url = _this.axios.urls.VUE_ASYN_REQ;
_this.axios.post(url, {
param: payload.PersonName
}).then(resp => {
//通过resp响应对象获取返回的数据,并赋予payload
payload.PersonName = resp.data.data;
//提交一个mutation,用于设置state中的参数。
context.commit('setPersonName', payload);
}).catch(error => {
})
}
......
}
<template>
<div>
<h1>首页</h1>
<p>
{{personName}}
</p>
<p>
<button @click="setPersonName">异步设置人员名称</button>
<button @click="doAjaxReq">异步获取后台数据</button>
</p>
</div>
</template>
<script>
export default {
name: 'Home',
data: function() {
return {
}
},
computed: {
personName: function() {
return this.$store.getters.getPersonName;
}
},
methods: {
setPersonName: function() {
//调用定义在actions中的方法,调用方式如下:
//this.$store.dispatch(type,payload);
//第一个参数type: 定义在actions中的方法名
//第二个参数payload:负载(一个json对象),即要传递的参数
this.$store.dispatch('setPersonNameAsyn', {
PersonName: '关羽'
});
},
doAjaxReq: function() {
//因为在actions中需要使用vue实例中的this参数,
//所以在此处将this参数作为负载传入。
this.$store.dispatch('getDataFromApiAsyn', {
PersonName: '赵云',
_this: this
});
}
}
}
</script>
<style>
</style>
图二:
还可以使用如下方式获取模块中的值:this.$store.state.userMsg.userName; 但不建议使用。
Vuex 解决了多视图之间的数据共享问题。但是运用过程中又带来了一个新的问题是,Vuex 的状态存储并不能持久化。也就是说当你存储在 Vuex 中的 store 里的数据,只要一刷新页面,数据就丢失了。 引入vuex-persist 插件,它就是为 Vuex 持久化存储而生的一个插件。不需要你手动存取 storage ,而是直接将状态保存至 cookie 或者 localStorage 中。
npm install vuex-persist -S