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:'sum',school:'school',subject:'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:'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:{ ...mapMutations({increment:'JIA',decrement:'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:'jiaOdd',incrementWait:'jiaWait'})
...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