vuex中的四个map方法的使用

vc组件 -> Dispatch -> Actions -> Commit -> Mutations -> Mutate -> State -> Render -> vc组件

state

store目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const state = {
// 这里就是存储数据的仓库,整体是一个对象,里面可以存储任何类型的,
可以继续,对象嵌套,数组嵌套 等等。
};

const mutations = {
// 这里是个对象,定义很多 mutations 方法, mutations是同步的
};

const actions = {
// 这里是个对象,定义很多 actions 方法, actions 里面可以异步

};
const getters = {
// 这里是个对象,定义很多 getter 方法
};

// 重要的就是这里
export default {
state, mutations, actions, getters
};

创建new Vuex.Store实例。

1
2
3
4
5
6
7
8
9
10
11
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
state,
actions,
mutations,
getters

})

当仓库比较复杂时候,可以分类存放 store,按照不同模块分开存放 ,最后使用 modules 汇总到一起。

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
import Vue from "vue";
import Vuex from "vuex";

import home from './Home'
// 这个是 home 模块的 store, 里面也是 有 export default { state, mutations, actions, getters }; 这四个东西。
// 最终引用的 时候 state.home.xxx 这样来引用。 this.$store.state.home.xxx;

import search from './Search'
import detail from './Detail'
import shopcart from './ShopCart'
import user from './User'
import trade from './Trade'

Vue.use(Vuex);

export default new Vuex.Store({
modules: {
home,
search,
detail,
shopcart,
user,
trade
}
});

mapState方法

导入

1
import {mapState, mapGetters, mapActions, mapMutations} from 'vuex'

mapState方法:用于帮助我们映射state中的数据为计算属性

1
2
3
4
5
6
7
computed: {
//借助mapState生成计算属性:sum、school、subject(对象写法)
...mapState({sum:'sum',school:'school',subject:'subject'}),

//借助mapState生成计算属性:sum、school、subject(数组写法)
...mapState(['sum','school','subject']),
},

如果直接这样写,计算属性只能是 mapState 里面的了,这时候我们就需要es6中的展开运算符:…。将mapState映射的计算属性追加上去,而不是覆盖原有的

1
computed: mapState(['likes','friends','token','userInfo']),
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
computed:{
// 直接使用,没有使用 mapState 辅助函数的清空
userInfo(){
return this.$store.state.userInfo
},

// 映射的 可以是一个 函数
...mapState({
userInfo: (state) => {
return state.userInfo;
}
}),

// 或者直接这样映射过来
...mapState(['userInfo','school','subject']),
// 有时候,我们映射的时候,想给计算属性起个新的名字,不想用原有的属性名,那就可以像下面这样做,用对象的形式去别名
...mapState({
myLikes:"likes",
myFriends:"friends",
theUserInfo:"userInfo"
})
}

Getters

有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数:

1
2
3
4
5
6
7
computed: {
doneTodosCount () {
// 通过 store的getters方法就可以把这个复杂的计算 统一 放到store里面了,哪里都可以使用到了。
return this.$store.state.todos.filter(todo => todo.done).length
}
}

如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。

Getter 接受 state 作为其第一个参数:

1
2
3
4
5
6
7
8
9
10
11
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos (state) { // Getter 接受 state 作为其第一个参数:这个state 就是上面定义的 state 数据仓库。
return state.todos.filter(todo => todo.done)
}
}

通过属性访问

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

1
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]

Getter 也可以接受其他 getter 作为第二个参数:

1
2
3
4
5
6
7
getters: {
// ... 这里实现了 getters 调用 其他getters方法。
doneTodosCount (state, getters) {
return getters.doneTodos.length
}
}

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

1
store.getters.doneTodosCount // -> 1

在组件中 直接 使用 store 中的 getter :当然也可以借助 mapGetters 统一导入计算属性中来使用。

1
2
3
4
5
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}

通过方法访问

你也可以通过让 getter 返回一个函数,来实现给 getter 传参。
在你对 store 里的数组进行查询时非常有用。

1
2
3
4
5
6
7
8
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}


1
2
3

store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }

mapGetters方法

mapGetters方法:用于帮助我们映射getters中的数据为计算属性

getters相当于vue中的计算属性,能拿到state里面最新的值

1
2
3
4
5
6
7
computed: {
//借助mapGetters生成计算属性:bigSum(对象写法)
...mapGetters({bigSum:'bigSum'}),

//借助mapGetters生成计算属性:bigSum(数组写法)
...mapGetters(['bigSum'])
},

Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
Vuex 中的 mutation 非常类似于事件:
每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。
这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

1
2
3
4
5
6
7
8
9
10
11
12
13

state: {
count: 1
},
mutations: {
increment (state) { // 事件类型 (type) 就是 increment。 后面的就是回调函数 (handler)
// 变更状态
state.count++
}


}

你不能直接调用一个 mutation 处理函数。这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation 处理函数,你需要以相应的 type 调用 store.commit 方法:

1
store.commit('increment')

你可以向 store.commit 传入额外的参数,即 mutation 的载荷(payload):

1
2
3
4
5
6
7
// ...
mutations: {
increment (state, n) {
state.count += n
}
}

你可以向 store.commit 传入额外的参数

1
store.commit('increment', 10)

在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:

1
2
3
4
5
6
// ...
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}

参数是一个对象的形式

1
2
3
4
store.commit('increment', {
amount: 10
})

提交方式也可以变成对象风格

1
2
3
4
store.commit({
type: 'increment',
amount: 10
})

当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此处理函数保持不变:

使用常量替代 Mutation 事件类型

常亮来自另外一个文件,单独定义

1
2
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// store.js
import { createStore } from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = createStore({
state: { ... },
mutations: {
// 我们可以使用 ES2015 风格的计算属性命名功能
// 来使用一个常量作为函数名, 注意这里的 方括号写法!!!!!
[SOME_MUTATION] (state) {
// 修改 state
}
}
})

Mutation 必须是同步函数

一条重要的原则就是要记住 mutation 必须是同步函数。为什么?请参考下面的例子:

1
2
https://vuex.vuejs.org/zh/guide/mutations.html#mutation-%E5%BF%85%E9%A1%BB%E6%98%AF%E5%90%8C%E6%AD%A5%E5%87%BD%E6%95%B0

在组件中提交 Mutation

你可以在组件中使用 this.$store.commit(‘xxx’) 提交 mutation,

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

1
2
https://vuex.vuejs.org/zh/guide/mutations.html#%E5%9C%A8%E7%BB%84%E4%BB%B6%E4%B8%AD%E6%8F%90%E4%BA%A4-mutation

mapMutations方法

mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数

跟mapState、mapGetters一样

不同的是,mapMutations是将所有mutations里面的方法映射为实例methods里面的方法

1
2
3
4
5
6
7
methods:{
//靠mapActions生成:increment、decrement(对象形式)
...mapMutations({increment:'JIA',decrement:'JIAN'}),

//靠mapMutations生成:JIA、JIAN(对象形式)
...mapMutations(['JIA','JIAN']),
}

使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用

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

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

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

Action

Action 类似于 mutation,不同在于:

Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}

// 也可以这样写参数, 第一个参数利用解构赋值,只拿到commit 函数就行,第二个参数是 载荷payload。
increment ({commit, params}) {
commit('increment')
}
}

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters

分发 Action

Action 通过 store.dispatch 方法触发:

1
2
3

store.dispatch('increment')

乍一眼看上去感觉多此一举,我们直接分发 mutation 岂不更方便?实际上并非如此,还记得 mutation 必须同步执行这个限制么?Action 就不受约束!我们可以在 action 内部执行异步操作:

1
2
3
4
5
6
7
8
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}

Actions 支持同样的载荷方式和对象方式进行分发:

参数是一个对象的方式:

1
2
3
4
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})

提交方式也可以变成对象风格:

1
2
3
4
5
6
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})

来看一个更加实际的购物车示例,涉及到调用异步 API 和分发多重 mutation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
actions: {
checkout ({ commit, state }, products) {
// 把当前购物车的物品备份起来
const savedCartItems = [...state.cart.added]
// 发出结账请求
// 然后乐观地清空购物车
commit(types.CHECKOUT_REQUEST)
// 购物 API 接受一个成功回调和一个失败回调
shop.buyProducts(
products,
// 成功操作
() => commit(types.CHECKOUT_SUCCESS),
// 失败操作
() => commit(types.CHECKOUT_FAILURE, savedCartItems)
)
}
}

组合 Action

https://vuex.vuejs.org/zh/guide/actions.html#%E7%BB%84%E5%90%88-action

1
2
3
4
5
6
7
8
9
10
11
// 假设 getData() 和 getOtherData() 返回的是 Promise

actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}

你在组件中使用 this.$store.dispatch(‘xxx’) 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store):

mapActions方法

mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

action类似于mutation

我们只需要记住一下几点:

action可以提交mutation,然后mutation去更改state
action不要直接去操作state,而是去操作mutation
action包含异步操作,类似于axios请求,可以都放在action中写
action中的方法默认的就是异步,并且返回promise

1
2
3
4
5
6
7
methods:{
//靠mapActions生成:incrementOdd、incrementWait(对象形式)
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

//靠mapActions生成:incrementOdd、incrementWait(数组形式)
...mapActions(['jiaOdd','jiaWait'])
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// ...
methods: {
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`

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

Module

https://vuex.vuejs.org/zh/guide/modules.html

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。
每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块
——从上至下进行同样方式的分割:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}

const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}

const store = createStore({
modules: { // 所有子模块 最终 在这里 汇总起来。
a: moduleA,
b: moduleB
}
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

参考文档

https://vuex.vuejs.org/zh/guide/state.html

https://vuex.vuejs.org/zh/guide/getters.html

https://vuex.vuejs.org/zh/guide/mutations.html

https://vuex.vuejs.org/zh/guide/actions.html

https://www.cnblogs.com/jinkai123/p/15889180.html