谈谈vuex

简介

 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
 Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新
 我们不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。

安装

 vuex的安装

1
2
3
4
5
//npm 方式引入
npm install vuex --save

//yarn 方式引入
yarn add vuex

vuex 基本概念

vuex最主要的几个核心概念就是State,Getter,Mutations,Actions

(一) State

state就是Vuex中的公共的状态, 可以将state看作是所有组件的data, 用于保存所有组件的公共数据,这里面的数据是响应式的,当很多地方引用了同state数据源并且未对数据进行深拷贝,那么当一个地方改变该数据,所有引用的地方数据都会发生变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
//定义
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)

const store = new Vuex.Store({
state:{
todoList:[1,2,3]
}
})

//组件获取
this.$store.state.todoList




(二) Getter

Getter是对state里面的数据进行预加工处理的函数方法,当我们需要对原始数据进行一个处理时,我们就需要用到getter,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const store = new Vuex.Store({
state: {
todoList: [
{ time: 1, text: 'do some'},
{ time: 2, text: 'do some' }
]
},
getters: {
//这里我们返回数据的长度
dealTodo: state => {
return state.todoList.length
}
}
})

Getter 会暴露为 store.getters 对象,我们可以以属性的形式访问这些值:

1
2
//使用
this.$store.getters.dealTodo

mapGetters 辅助函数
当getters里面对象过多时,我们可以以ES6的语法形式解析里面的对象,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性computed:

1
2
3
4
5
6
7
8
9
10
11
12
import { mapGetters } from 'vuex'

export default {
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'getters1',
'getters2',
'getters3',
])
}
}

当然我们也可以使用对象的形式放入

1
2
3
mapGetters({
dataCount: 'getters1'
})




(三) Mutations

Mutations是更改 Vuex 的 store 中的状态的唯一方法,且不能用于处理异步事件,其中第一个参数是state, 第二参数是payload, 也就是传入数据

1
2
3
4
5
6
7
8
9
10
11
12
//实例
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state,payload) {
// 变更状态
state.count += payload.amount
}
}
})

此时我们不能直接调用store里面的mutations实例方法,而是要通过commit方式对应的type来触发该方法

1
2
3
4
5
6
7
8
9
10
//使用 

//方式1
this.$store.commit('increment',{amount:100})

//方式2 直接使用包含 type 属性的对象
this.$store.commit({
type: 'increment',
amount: 10
})

Mutation 必须是同步函数,再次强调一下,所以存在异步操作的函数最好不要使用mutation里面的方法

mapMutations辅助函数
mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { mapMutations } from 'vuex'

export default {
methods: {
//方式1
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`

// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),

//方式2
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}




(四) Action

action 类似于 mutation,不同在于:

  • action 提交的是 mutation,而不是直接变更状态。
  • action 可以包含任意异步操作。
  • actions中的回调函数的第一个参数是context, 是一个与store实例具有相同属性和方法的对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    const store = new Vuex.Store({
    state: {
    count: 0
    },
    mutations: {
    increment (state) {
    state.count++
    }
    },
    actions: {
    //写法1
    increment (context) {
    context.commit('increment')
    },

    //写法2 我们也可以使用ES6语法
    increment ({state,commit}) {
    commit('increment')
    }
    }
    })

action的触发,当然action也和mutation的触发方式差不多,也同样支持包含 type 属性的对象方式分发,下面我们就简单写一种,当然关键字不一样,这个触发关键字是dispatch

1
this.$store.dispatch('increment',data)

在上面我们也说到action可以进行异步操作,那么我们就可以以promise为例简单实现一下

1
2
3
4
5
6
7
8
9
10
11
const store = {
//...
actions:{
increment({commit},data){
return new Promise((resolve)=>{
commit('increment',data)
resolve()
})
}
}
}

mapActions辅助函数
mapActions 辅助函数将组件中的 methods 映射为 store.dispatch 调用(需要在根节点注入 store)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { mapActions } from 'vuex'

export default {
// ...
methods: {
...mapActions([
// 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
'increment',

// `mapActions` 也支持载荷:
// 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
'incrementBy'
]),
...mapActions({
// 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
add: 'increment'
})
}
}




vuex项目结构拆分

当项目store文件很大的时候,我们就不得不拆分项目的目录结构,大体上我们必须准守以下规则

  • 应用层级的状态应该集中到单个 store 对象中。
  • 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
  • 异步逻辑都应该封装到 action 里面。

只要遵循上面的规则,代码可以任意组织,下面是我经常性使用的组合形式

1
2
3
4
5
6
7
8
9
10
11
└── store
├── index.js
├── Type.js
├── binding
│   ├── actions.js
│   ├── index.js
│   └── mutations.js
├── house
   ├── actions.js
   ├── index.js
   └── mutations.js

store的根目录index.js用于整合各个局部状态store,使用modules整合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//文件 store/index.js

import Vuex from 'vuex'
import Vue from 'vue'
import houseStore from './house'
import bindingStore from './binding'
Vue.use(Vuex)

const store = new Vuex.Store({
modules:{
house:houseStore, //house store
binding:bindingStore //binding store
}
})

export default store

Type.js主要用于存储事件type名

1
2
3
4
5
6
//Type.js
//例如...
//操作1
export const RRDUCELIST = 'RRDUCELIST'
//操作2
export const ADDLIST = 'ADDLIST'

局部store我就以binding为例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

/*----------------------------index.js-----------------------------*/
//index.js
import {RRDUCELIST} from '@/store/Type.js'
import mutations from './mutations'
import actions from './actions'
const index = {
state:{
tableDta:[
{data:['数据1','数据2','数据3']},
]
},
mutations,
actions,
}
export default index


/*----------------------------mutations.js-----------------------------*/
//mutations
import {RRDUCELIST,ADDLIST} from '@/store/Type.js'
const mutations = {
//删除数据
[RRDUCELIST](state, payload) {
state.tableDta.splice(0, 1)
},
//增加数据
[ADDLIST](state,payload){
state.tableDta.push(payload)
}
}
export default mutations


/*----------------------------actions.js-----------------------------*/
//actions
import {RRDUCELIST,ADDLIST} from '@/store/Type.js'
const actions = {
reduceListAction({commit,state},index){
return new Promise((resolve, reject) => {
commit(RRDUCELIST,index)
resolve()
})
},
addListAction({state,commit},data){
commit(ADDLIST,data)
}
}
export default actions