为什么要使用状态管理?
在开发整站时会有很多数据,也是由许多组件拼成,会共享一些状态,如用户的登录状态,或购物车信息在头部用到了,或在页面等其他位置也有使用到。我在其中一个位置进行增删,相应的在其他位置的购物车信息也要更新。直接的做法是通过事件,如果有其他触发了更新无论是通过计算属性还是watch。需要通过事件和eventBus来传到另外一个组件,这个缺点是当我们的状态非常复杂,组件非常多时,我们需要挨个依次去通知组件状态更新,这样会变得很复杂。所以就产生了状态管理模型。
状态管理的模型
我们有一个统一的数据中心store,用来维护状态数据,每个组件进行更新时就通知,每个组件更新时通知数据中心,数据中心将这样的共享状态再去触发调用它每一个组件的更新,这是一个数据管理模式。
状态管理机制
在Vuex的设计概念中,Vue组件通过Actions的这个动作,Actions去操作mutations,mutations去控制数据中心的状态State,状态更改以后再反映到页面的组件的一个渲染。
在下图的展示为虚线框的为Vuex的操作过程。
Actions可以进行异步操作,比如调用后台数据等异步操作只能在Acitions中进行。
Mutations是同步地更改State数据的一种方法,与Devtools等进行交互。
在这个模型内要保证数据的单向流动,Vue Components通过dispatch去触发Actions,Acitions通过commit来触发mutations,mutations直接操作我们的数据State。
state是动态更新,action是动态操作。能改数据的只有mutations。
如果项目复杂的话,可是使用Vuex,会使项目非常高效。如果是简单的项目,没必要使用这个复杂的事件管理机制。
安装vuex
三种选一种即可:
npm install vuex --save
cnpm install vuex --save
yarn add vuex
在安装完成后进行使用。
需要创建store文件夹,新建store.js文件
在store.js中导入Vuex,必须使用Vue.use(Vuex); 进行挂载,并实例化一个store。
import Vuex from 'vuex'
Vue.use(Vuex);
实例化一个Store:
let store = new Vuex.Store({
state: {
totalPrice: 0
},
mutations: {
increment(state, price){
state.totalPrice += price
},
decrement(state, price){
state.totalPrice -= price
}
}
})
在实例化完成后一定要在全局使用这个store,这里是我的存放store.js的路径,需要你修改为自己的路径:
import store from './store/store.js';
new Vue({
store,
render: h => h(App),
}).$mount('#app')
然后在每个子组件内就可以通过 this.$store去触发里面的方法,或调用里面的状态。
Mutations
在子组件内的调用:
methods:{
addOne(){
this.$store.commit('increment',this.price)
},
minusOne(){
this.$store.commit('decrement',this.price)
}
}
可以看到在子组件内调用时,通过事件内的 this.$store.commit 提交 mutations内的 increment 事件,来修改价格;或者通过子组件内的computed属性获取并计算价格,这两个方法均可修改totalPrice状态值,方法二如下方示例:
computed: {
totalPrice(){
return this.$store.state.totalPrice
}
}
上面的方法均可单独修改完成。
优点:不需要调用各种状态及各种事件,只需要调用数据中心中对应的mutations,就可以实现模块间数据共享。
Actions
当在变量store内添加actions:
actions:{
increase(conext, price){
context.commit('increment', price)
}
}
相应的,在子组件内调用方法修改时需要改为:
methods:{
addOne(){
this.$store.dispatch('increment',this.price)
},
minusOne(){
this.$store.dispatch('decrement',this.price)
}
}
效果与上方未添加actions时的效果一致:
这个方法是子组件 dispatch 了一个actions,actions对事件 increment 进行提交,等于是actions作为一个中介进行传递。
mutations和actions的区别:
- actions是先进行异步操作,然后触发mutations
- mutations是同步操作数据的一个方式
- 修改mutations是commit(),修改actions是dispatch()
mutations是不可以发送http请求回调的。
Getters
在store实例内添加getters属性,
getters:{
getTotal(state){
return state.totalPrice
}
}
后面在需要获取totalPrice的地方,可以改为 this.$store.getters.getTotal 最后拿到totalPrice。
Module
可以把状态集分成不同的模型,每一个模型都维护自己的一套数据:
const moduleA = {
state: {...},
mutations: {...},
actions: {...},
getters: {...}
}
const moduleB = {
state: {...},
mutations: {...},
actions: {...},
getters: {...}
}
const store = new Vuex.store({
modules:{
a: moduleA,
b: moduleB
}
})
store.state.a // -> modulesA's state
store.state.b // -> modulesB's state
使用Vuex需要注意的问题:
- 将所有应用层级的状态集中到同一个store内。
- 更改层级的状态只能通过提交(commit) mutation进行更改,mutations是同步的。
- 如果需要提交异步的东西,我们需要把逻辑放到actions内部。
State内存储的是全局的一些状态,state的改变会同步展现到组件内。
官方在使用store时,统一对外的接口是 index.js:
在store内存在state,mutations,actions,getters四个属性,可进行使用。
这个是上述部分内完整的store实例如下:
let store = new Vuex.Store({
state: {
totalPrice: 0
},
getters:{
getTotal(state){
return state.totalPrice
}
},
mutations: {
increment(state, price){
state.totalPrice += price
},
decrement(state, price){
state.totalPrice -= price
}
},
actions:{
increase(conext, price){
context.commit('increment', price)
}
}
})
在521这一天 进行二次维护:
这个是重新添加的store.js文件及使用store的组件的内容,会更完整一些:
store.js:
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 100000,
isLogin: false //登录状态
},
mutations: {
increase(state){
state.count += 1;
},
Login(state){
state.isLogin = true;
},
logout(state){
state.isLogin = false;
}
},
getters:{ //对state中的数据进行加工处理
money: state => {
return state.count + '元'
}
},
actions: { //异步操作时 可以写接后台接口的这个位置
increaseAsync({commit}){
setTimeout(() => {
commit('increase');
},1000);
},
submitLogin({commit}){
return new Promise( resolve => {
setTimeout(()=> {
commit('login');
resolve(true);
},2000);
})
}
}
})
export default store;
可以看到actions是提交的mutations内的内容。
使用store的组件:
<template>
<div>
<h3>Dashboard</h3>
<!-- 父组件必须有一个路由的出口,负责显示子路由 -->
<router-view></router-view>
<button @click="inc">increease</button>
<button @click="incAsync">incAsync</button>
<p>{{count}}</p>
<p>您的余额为:{{money}}</p>
</div>
</template>
<script>
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex';
export default {
methods: {
...mapMutations(['increase']),
...mapActions(['increaseAsync']),
inc() {
this.$store.commit('increase');
// this.increase()
},
incAsync() {
this.$store.dispatch('increaseAsync');
// this.increaseAsync()
}
},
computed: {
// mapState是个函数,最终会返回一个对象
// 下面的等价于{count: this.$store.state.count}
...mapState(['count']), //是count
...mapGetters(['money'])
},
}
</script>
<style lang="scss" scoped>
</style>
使用时:
this.$store.commit('increase'); 通过提交muations修改store内state中保存的值。
this.$store.dispatch('increaseAsync');通过提交actions修改store内state中保存的值。