这两天在做Vue移动端的项目,正好用到了Vuex,记录一下相关知识。
一、安装
- npm
npm install vuex --save
- yarn
yarn add vuex
Vuex是Vue.js 的SPA单页组件化应用程序开发的状态管理模式插件。由于Vue SPA应用的模块化,每个组件都有它各自的数据(state)、界面(view)、和方法(actions)。这些数据、界面和方法分布在各个组件中,当项目内容变得越来越多时,每个组件中的状态会变得很难管理:
- 保持对所有的事件追踪将变得很困难。到底哪个事件是哪个组件派发的,哪个组件该监听哪个事件?
- 项目逻辑分散在各个组件当中,很容易导致逻辑的混乱,不利于我们项目的维护。
- 父组件将变得和子组件耦合越来越严重,因为它需要明确的派发和监听子组件的某些事件。
所以在vue项目中组件间相互传值或者后台获取的数据需要供多个组件使用的情况很多的话,有必要考虑引入vuex来管理这些凌乱的状态。个人认为它就是决解模块之间的数据通信而存在的,和Redux的思想差不多。
二、Vuex的四个核心概念
上图从左到右,从组件出发,在组件的created中提交dispatch,通过action可以和后台数据交互,比如获取初始化的数据源,或者中间数据的过滤等;然后在 action中去派发 Mutation,触发state状态的改变;最后在组件的计算属性中获取state数据渲染在页面上,视图更新。
- State
Vuex使用单一状态树,每个应用仅仅包含一个store实例,因为单一的状态树,可以让我们定位到每一个特定的状态片段,便于调试。
可以在store的index.js文件中定义一些状态。
const state = {
playing: false,
nowsong: '',
audioProgss: ''
}
那么如何在组件中获得状态呢?
- 从 store 实例中读取状态,也就是在计算属性中返回某个状态。
Vuex 通过 store 选项,提供了一种机制将状态从根组件注入到每一个子组件中(需调用 Vue.use(Vuex))。可以在在main.js文件中引入:
import store from './store'
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store 访问到。
computed : {
audioActived : function() {
return this.$store.state.playing
},
nowsong : function () {
return this.$store.state.nowsong
},
audioProgss : function () {
let progess = this.$store.state.audioProgss
if (parseInt(progess) >= 100) {
this.$emit('nextplay')
}
return progess
}
},
- mapState辅助函数
当组件中需要多个状态时,将这些状态声明为计算属性,会使代码变得冗余。这里可以使用mapState辅助函数生成计算属性。
import { mapState } from 'vuex'
computed : {
...mapState({
showlist : state => state.playlistToggle,
playlist : state => state.playlist,
nowsong : state => state.nowsong
})
},
当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState传一个字符串数组。
import { mapState } from 'vuex'
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
])
- Getters
- 创建Getters
有时需要从state中派生出某些状态,这时就可以用到Getters。在store下面建一个getters的文件夹,用来从state中获取数据。Getters接受state作为其第一个参数。
export default const stop = state => {
if(state.playing === false) {
return true
}
}
- 获取Getters
Getters会暴露为store.Getters对象,所以可以在组件中很方便的使用它。
import {mapGetters} from 'vuex'
computed: {
...mapGetters([
'stop'
])
},
- Mutations
要改变Vuex中store的状态唯一的方法就是通过触发mutations,每个 mutation都有一个字符串的事件类型(使用常量) 和 一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。
mutations:{
[REFRESH_DATA] (state, data) {
state.data = data
}
}
当触发一个类型为REFRESH_DATA
的mutations时,会调用此函数。要使状态发生改变,还需要用到commit,即用来说明一个使 mutation 状态发生改变的操作。
为了能用devtools追踪到状态的变化,mutations必须是同步的。
store.commit('REFRESH_DATA')
- action
- 创建action
action 定义为一个类似事件监听器的模式而不是直接调用它们,提交的是mutations,而且可以包含任意异步操作。
increTranStyle (context, style) {
return new Promise((resolve,reject) => {
context.commit('increTranStyle',style)
if(context.state.tranStyle === style) {
resolve()
}
})
}
- 分发action
可以在Vuex组件的created中分发dispatch,去触发这个事件。和mutations的commit不同,action使用的是dispatch,可以在组件的created中分发dispatch。
created() {
this.$store.dispatch('increTranStyle');
}
当然也可以使用 mapActions
辅助函数,将组件的 methods 映射为store.dispatch调用。
import {mapActions} from 'vuex'
methods: {
...mapActions([
'getData'
])
},
三、使用举例
这里我是把mutations和action以及getters分别放在不同的文件夹中,当然也可以把它们放在一个modules文件里面。
- 首先在项目的入口文件main.js中引入store:
import store from './store'
并且将store挂载在vue上:
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
- store的index.js文件
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations'
import actions from './action'
import * as getters from './getters'
import ajax from '../ajax'
Vue.use(Vuex)
const state = {
getBaseMsg: {
title: '',
features: [],
retreat: '',
}
}
export default new Vuex.Store({
state,
actions,
getters,
mutations,
})
- 在需要用到state的组件中组件中的中的created分发第一个dispatch。
created() {
this.$store.dispatch('getBaseMsg');
}
当然也可以通过mapActions辅助函数,上面已经提到过。
- 在对应的store下面编写getters、mutations、actions。
- actions
import ajax from '../ajax'
export default {
getData({ commit, state }) { //进入action
ajax('GET', 'http://localhost:9090/info').
then(res => { // action中调用封装后的ajax成功
commit('GET_DATA', {
res
})
})
}
}
- getters
export const getBaseMsg = state => state.getBaseMsg
- mutations
const GET_DATA = 'GET_DATA'
export default {
[GET_DATA](state, payload) { // 进入mutation
state.getBaseMsg = payload.res; //进入mutations修改state成功
}
}
- 最后在需要获取state的组件中使用mapGetters获取对应的state,管理数据非常方便。
import { mapGetters } from 'vuex';
export default {
...
computed: {
...mapGetters([
'getBaseMsg'
])
},
...
}
四、总结
- 整个数据流都是单向的。
- 组件可以调用action。
- action用来派发 Mutation。
- 只有 mutation 可以改变状态。
- dispatch用来派发一个action,表明有某些事件发生的意向;而commit用来说明一个使mutation状态发生改变的操作,会使实际状态发生改变的同步操作。
- store 是响应式的,无论state什么时候更新,组件都将同步更新。