从0学习Vue3(12)

92 阅读4分钟
4.3.4.3 Mutation

提交载荷(Payload)

听起来好像很复杂,举个栗子就知道了:

我们在mutations中定义一个函数,需要从组件传参来执行,参数即是载荷

 const store = createStore({
   state: {
     count: 1
   },
   mutations: {
     increment(state,n){ //这个n呢就是需要从组件传过来的参数了
       state.count += n
     }
   }
 })

组件中使用increment:

 store.commit('increment',10)

而如果我们在后续,需要往这一个函数里面继续加参数,那这个函数写起来岂不是很长,像这样increment(state,n,m,o,p,q,r,s,t)非常怪异。所以载荷一般会写成对象:

 const store = createStore({
   state: {
     count: 1,
     name: ''
   },
   mutations: {
     increment(state,payload){ //这个n呢就是需要从组件传过来的参数了
       state.count += payload.a
       name = payload.b
       ......
     }
   }
 })

在组件中调用:

 store.commit('increment',{
     a:1,
     b:'芜湖'
 })

对象风格的提交方式

提交mutation的另一种方式是直接使用包含type属性的对象,还拿上面那个mutation为例子:

 store.commit({
     type:'increment',
     a:1,
     b:'芜湖'
 })

使用常量代替 Mutation 事件类型

把mutaion时间统一到一个js文件中管理,在多人协作的大项目中会比较有帮助:

 // mutation-types.js
 export const SOME_MUTATION = 'SOME_MUTATION'
 // store.js
 import { createStore } from 'vuex'
 import { SOME_MUTATION } from './mutation-types'
 ​
 const store = createStore({
   state: { ... },
   mutations: {
     // 我们可以使用 ES2015 风格的计算属性命名功能
     // 来使用一个常量作为函数名
     [SOME_MUTATION] (state) {
       // 修改 state
       ......
     }
   }
 })

在组件中使用:

 import { SOME_MUTATION } from './mutation-types' //同样要引入
 //使用
 store.commit(SOME_MUTATION)

在mutation中必须写同步函数,mutation中的异步函数会让devtools捕捉不到它的改变。但也有相对应专门来写异步函数的地方 action。

那么跟StateGetter一样,他们有mapStatemapGetters,mutation也有mapMutations

 methods:{
     //第一种写法  数组,这样写 使用的时候就是  this.increment()
     ...mapMutations(['increment'])
     //第二种写法  载荷,这样写 使用的时候就是  this.add()
     ...mapMutations({
         add:'increment'
     })
 }
4.3.4.4 Action

Action 类似于 mutation, 不同点在于:

  • Action 提交的是 mutation,而不是直接变更状态
  • Action 可以包含任意异步操作

写个小栗子:

 const store = createStore({
   state: {
     count: 0
   },
   mutations: {
     increment (state) {
       state.count++
     }
   },
   actions: {
     increment (context) {
       context.commit('increment')
     }
   }
 })

实践中,我们会经常用到ES2015的解构来简化代码:

 actions: {
   increment ({context}) {
     commit('increment')
   }
 }

分发Action,我们回忆mutation的调用是state.commit('increment'),action的触发方式是store.dispatch('increment')。到目前位置都和mutation没有本质上的区别,但上面我们说过了。Action内部可以执行异步操作:

 actions:{
   incrementAsync ({ commit }) {
     setTimeout(() => {
       commit('increment')
     }, 1000)
   }
 }

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

 // 以载荷形式分发
 store.dispatch('incrementAsync', {
   amount: 10
 })
 ​
 // 以对象形式分发
 store.dispatch({
   type: 'incrementAsync',
   amount: 10
 })

同样的,Action也有mapActions

 import { mapActions } from 'vuex'
 ​
 export default {
   methods: {
     //第一种写法   使用时是:this.increment()
     ...mapActions(['increment']),
     //第一种写法   使用时是:this.add()
     ...mapActions({
       add: 'increment'
     })
   }
 }
4.3.4.5 Module

当我们的项目逐渐壮大,在state中需要管理的数据就会越来越多,如果全部放在一起的话,到时候就非常地乱不好管理,store对象就有可能变得相当臃肿。

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

 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 的状态

这里可以先了解一下Vuex进阶的项目结构:

Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:

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

只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。

 ├── index.html
 ├── main.js
 ├── api
 │   └── ... # 抽取出API请求
 ├── components
 │   ├── App.vue
 │   └── ...
 └── store
     ├── index.js          # 我们组装模块并导出 store 的地方
     ├── actions.js        # 根级别的 action
     ├── mutations.js      # 根级别的 mutation
     └── modules
         ├── moduleOne.js       # 模块一
         └── moduleTwo.js       # 模块二

像结构中,我们在store文件夹中再创建一个modules文件夹,该文件夹下便可以放store的各个模块。store文件夹下分别有actions.js,mutations.js。分别接收模块中的action、mutation。而里面的index.js正是将所有模板聚集的地方,举个栗子:

 //index.js
 import {createStore} from 'vuex'
 import moduleOne from '@/store/modules/moduleOne'
 import moduleTwo from '@/store/modules/moduleTwo'
 import actions from '@/store/actions'
 import mutations from '@/store/mutations'
 ​
 const store = createStore({
     modules:{
         a:moduleOne,
         b:moduleTwo
     },
     actions,     //moduleOne和moduleTwo的acttion都放到这个actions里
     mutations    //moduleOne和moduleTwo的mutation都放到这个mutations里
 })
 ​
 export default store

对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象

 //moduleOne.js
 const moduleA = {
   state: () => ({
     count: 0
   }),
   mutations: {
     increment (state) {
       // 这里的 `state` 对象是模块的局部状态
       state.count++
     }
   },
   getters: {
     doubleCount (state) {
       return state.count * 2
     }
   }
 }

同样,对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState

 const moduleA = {
   // ...
   actions: {
     incrementIfOddOnRootSum ({ state, commit, rootState }) {
       if ((state.count + rootState.count) % 2 === 1) {
         commit('increment')
       }
     }
   }
 }

对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:

 const moduleA = {
   // ...
   getters: {
     sumWithRootCount (state, getters, rootState) {
       return state.count + rootState.count
     }
   }
 }