什么是vuex
vuex是项目里面的状态管理器,统一管理和维护各个组件的可变化状态,vue的应用核心是store,vue有五个核心概念,state, getters, mutations, actions, modules。
核心概念
每一个 Vuex 应用的核心就是 store,里面又包括:
-
state,数据,用来存放数据源,就是公共状态 调用:this.$store.state.key -
getters,数据加工,有的时候需要对数据源进行加工,返回需要的数据 调用:this.$store.getters.fun -
mutations,执行,操作结束之后,actions通过commit更新state数据源 调用:this.$store.commit('mutation方法名') -
actions,事件,要执行的操作,可以进行同步或者异步事件。一般用来请求数据,并提交mutations调用:this.$store.dispatch('action方法名') -
modules,使用单一状态树,致使应用的全部状态集中到一个很大的对象,所以把每个模块的局部状态分装使每一个模块拥有本身的 state、mutation、action、getters、甚至是嵌套子模块;
工作流程
所以,简单的来说,vuex的工作流程就是:
(1)通过dispatch去提交一个actions
(2)actions接收到这个事件之后,在actions中可以执行一些异步/同步操作,根据不同的情况去分发给不同的mutations
(3)actions通过commit去触发mutations,
(4)mutations去更新state数据,state更新之后,就会通知vue进行渲染
创建一个 Store
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
let store = new Vuex.Store({
state: {},
getters: {},
mutations: {},
actions: {}
})
必须在 Vue.use(Vuex) 之后创建 store
用法说明
state用法说明
存储应用状态数据的对象,state的值可以是一个对象,也可以是一个返回对象的函数,类似vue中组件的data。
定义state
const store = new Vuex.Store({
state: {
userInfo: false
}
})
调用state
- 一般调用
this.$store.state.userInfo
问题:
state 的更新并不会更新视图
解决:
使用computed
<template>
<div class="home">
<h2>{{title}}</h2>
<div>{{content}}</div>
</div>
</template>
<script>
import store from '@/stores'
export default {
name: 'home',
computed: {
title() {
return store.state.title
},
content() {
return store.state.content
}
}
}
</script>
- 使用辅助函数
mapState调用 当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用mapState辅助函数帮助我们生成计算属性,让你少按几次键,通常我们把store的state通过mapState函数映射到组件的computed上
<template>
<div class="home">
<h2>{{title}}</h2>
<div>{{content}}</div>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: 'home',
computed: {
...mapState([
'title',
'content'
])
}
}
</script>
getters用法说明
定义一个函数的形式来返回派生数据,也就是state只能返回值,如果对值要进行一些转义,那么就用getters
定义getters
const store = new Vuex.Store({
state: {
userInfo: false
},
getters: {
userInfo: (state)=>{
return state.userInfo
},
isLogin: (state)=>{
return state.userInfo!==false
},
username: (state)=>{
let _username = ''
if(typeof state.userInfo === 'object'){
_username = state.userInfo.hasOwenProperty('username') ? state.userInfo.username : ''
}
return _username
},
}
})
调用getters
- 一般调用
this.$store.getters.isLogin
- 使用辅助函数
mapGetters调用 与mapState函数类似,通常映射到组件的computed上
mutations用法说明
更改Vuex的store中的状态的唯一方法是提交mutation,Vuex中的mutation非常类似于事件:每个mutation都有一个字符串的事件类型 (type)和 一个回调函数 (handler)
定义mutations
const store = new Vuex.Store({
state: {
userInfo: false
},
getters: {
userInfo: (state)=>{
return state.userInfo
},
isLogin: (state)=>{
return state.userInfo!==false
},
username: (state)=>{
let _username = ''
if(typeof state.userInfo === 'object'){
_username = state.userInfo.hasOwenProperty('username') ? state.userInfo.username : ''
}
return _username
},
},
mutations: {
setUserinfo: (stata, data){
stata.userInfo = data
localStorage.setItem('userInfo', data)
}
}
})
调用mutations
mutation函数必须是同步的
mutation中的函数不要直接调用
- 使用
commit调用
commit方法没有返回值
store.commit('type', payload)
// or
store.commit({
type1: payload1,
type2: payload2,
...:...
})
-- type要提交的mutation回调函数名称,type为固定的key
-- payload载荷:提交的额外数据,任意格式
- 使用辅助函数
mapMutations调用
import { mapMutations } from 'vuex' // 先从vuex里导入 mapMutations
methods:{
...mapMutations([
'setUseInfo', //将mutation里的方法映射到该组件内
'setToken' //等同于this.$store.commit('setToken')
]),
changeToken(token){
this.setToken(token) //由于上一步已经将mutation映射到组件内,所以组件可以直接调用setToken
}
changeUser(userInfo){
this.setUseInfo(userInfo) //同理
}
}
actions用法说明
action中的函数与mutation中的函数类似,但是它主要用来进行异步任务的处理,然后通过提交mutation来修改state
注意:
action中的函数不要直接修改state
定义actions
const store = new Vuex.Store({
state: {
userInfo: false,
token: ''
},
getters: {
userInfo: (state)=>{
return state.userInfo
},
isLogin: (state)=>{
return state.userInfo!==false
},
username: (state)=>{
let _username = ''
if(typeof state.userInfo === 'object'){
_username = state.userInfo.hasOwenProperty('username') ? state.userInfo.username : ''
}
return _username
},
},
mutations: {
setUserinfo: (stata, data){
stata.userInfo = data
localStorage.setItem('userInfo', data)
}
},
actions: {
initUserinfo: ({state, getters, commit, dispatch}, fromServer){
//state, getters, commit, dispatch使用哪个,引入哪个,但是不要在actions里面直接修改state
//commit对应mutations里面的方法
//dispatch对应actions里面的方法
if(fromServer){
// 自行引入http哦
http.get('member/userinfo', {token:state.token}).then(res=>{
if(!res.code){
commit('setUserinfo', res.data)
}
})
}else{
let userInfo = localStorage.getItem('userInfo')
if(userInfo) commit('setUserinfo', userInfo)
}
}
}
})
调用actions
action 任务需要通过 dispatch 方法来提交(派发),与 commit 类似
- 直接通过
dispatch调用dispatch方法有返回值,且一定返回一个promise对象
store.dispatch(type, payload)
// or
store.dispatch({
type1: payload1,
type2: payload2,
...:...
})
- 使用辅助函数
mapActions调用 与mapMutations函数类似,把组件的methods映射为store的actions的dispatch调用
小结
state、getters返回值,可以直接调用,但是需要页面同步更新需要写在vue的computed方法中
mutations=>commit、actions=>dispatch属于方法调用,可以直接调用,如果要直接在vue中使用通过辅助函数写在VUE的methods方法中
使用 store 注入调用
如果每个组件在使用store的时候都import会比较繁琐,这个时候,我们通过vuex提供的store选项把store对象注入到vue的原型上
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from '@/stores'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
配置注入后,我们就可以在组件实例中使用this.$store来访问store对象了
<template>
<div class="home">
<h2>{{title}}</h2>
<div>{{content}}</div>
</div>
</template>
<script>
// import store from '@/stores' // 可以去掉了
export default {
name: 'home',
computed: {
title() {
return this.$store.state.title
},
content() {
return this.$store.state.content
}
}
}
</script>
为什么不能在mutations执行异步操作?
Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过action来提交mutation实现,这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现time-travel了。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。