actions

Action is similar to mutation, except that

Mutation does not recommend placing asynchronous requests, whereas actions can hold asynchronous requests

In the action, after receiving the data from the asynchronous request, we need to pass the data to mutation via COMMIT,

Let mutation help us store the data in state

The basic use

store.js

actions: {
   / / parameter 1:
   Context is an object of type store
   // Context has property state, rootState to get data from state
   // ----- If there is no module, the value of rootState is the same as that of state
   // There are methods on the context:
   // 1. Actions - You can invoke an action event within another event
   // 2. Getters and rootGetters -- call methods in getters
   // ----- If there is no module, the value of getters is the same as that of rootGetterd
   // 3. Mutations -- Call the method in mutations

   / / parameter 2:
   // The argument passed in
   incrementNAction(context, payload) {
     // Use setTimeout to simulate an asynchronous request
     setTimeout(() = > context.commit('incrementN', payload), 1000)}}Copy the code

The caller

methods: {
  incrementN() {
    // Use dispath to distribute an actions
    // Parameter 1: event name
    // Parameter 2: parameter
    // if there are multiple parameters, you need to consolidate the parameters into one object before passing it
    // if there is no parameter, the second parameter can not be passed
    this.$store.dispatch('incrementNAction', {
      step: 10}}})Copy the code
methods: {
  incrementN() {
    // Dispatch, like mutation, supports passing events and parameters as objects
    this.$store.dispatch({
      type: 'incrementNAction'.step: 10}}})Copy the code

mapActions

Action also has a corresponding helper function, which is similar to mapMutations

An array of writing

. mapActions(['incrementNAction'.'decrementNAction'])
Copy the code

Object syntax

. mapActions({incrementNAction: 'incrementNAction'.decrementNAction: 'decrementNAction'
})
Copy the code

module

Because of the use of a single state tree, all the states of an application are grouped into one large object,

When the application becomes very complex, the Store object can become quite bloated

To solve the above problems, Vuex allows us to split the Store into modules.

Each module has its own state, mutation, action, getter, and even nested submodules (that is, modules of their own modules);

HomeModule. Js – module

// The essence of a module is an object
const home = {
  state() {
    return {
      msg: 'Home'}}}export default home
Copy the code

Store. js -- Aggregation module

import { createStore } from 'vuex'
import home from './modules/homeModule'

export default createStore({
  state() {
    return {
      msg: 'root'}},modules: {
    // key is the actual name you want to use
    // Value is the imported module object
    home
  }
})
Copy the code

The user

<template>
  <div>
    <h2>{{ $store.state.msg }}</h2>
    <h2>{{ $store.state.home.msg }}</h2>
  </div>
</template>
Copy the code

The namespace

By default, action and mutation within a module are still registered in the global namespace:

  • This enables multiple modules to respond to the same action or mutation or getters

homeModule.js

const home = {
  state() {
    return {
      count: 0}},mutations: {
    increment(state) {
      state.count++
    }
  }
}

export default home
Copy the code

store.js

import { createStore } from 'vuex'
import home from './modules/homeModule'

export default createStore({
  state() {
    return {
      count: 0}},mutations: {
    increment(state) {
      state.count++
    }
  },

  modules: {
    home
  }
})
Copy the code

The user

<template>
  <div>
    <h2>root count: {{ $store.state.count }}</h2>
    <! $store.state. Home returns the state function of the home module -->
    <h2>home count: {{ $store.state.home.count }}</h2>
    <button @click="() => $store.commit('increment')">+ 1</button>
  </div>
</template>
Copy the code

Now, when you click the button, you’ll see that the count of both the submodule and the root module has changed,

By default, commit does not differentiate modules exactly the way state does

This also applies to getters and Actions

<! By default, we can't distinguish getters from different modules. By default, vuex merges getters from all modules.
<h2>{{ $store.getters.doubleCount }}</h2>
Copy the code
<! Vuex consolidates actions for all modules by default -->
<button @click="() => $store.dispatch('incrementAction')">+1(action)</button>
Copy the code

We can enable module namespaces if we want modules to have higher encapsulation and reusability, or to be able to distinguish the corresponding methods in different modules

When a module has a namespace enabled, all its getters, actions, and mutations are automatically named based on the path registered by the module

<h2>root count: {{ $store.state.count }}</h2>
<h2>home count: {{ $store.state.home.count }}</h2>

<! Gettets passes the path id as an attribute of getters.
<h2>home getters count: {{ $store.getters['home/doubleCount'] }}</h2>

<! -- Path identifiers for commit and dispatch, passed as arguments to functions -->
<button @click="() => $store.commit('home/increment')">+1(mutations)</button>
<button @click="() => $store.dispatch('home/incrementAction')">+1(actions)</button>
Copy the code

Parameters that

getters: {
  // With the namespace enabled, the getters method in the module takes 2 more arguments than the original getters method
  
  /* roottState -- use the rootGetters property of the root store's state object -- call the getters method of the root store */
  doubleCount(state, getters, rootState, rootGetters) {
    return state.count * 2}}Copy the code
actions: {
  Action Regardless of whether the module is enabled, the context passed in has six properties that can be used

  // If the module is enabled then
  // Commit, state, dispatch, getters use the corresponding commit, state, dispatch, getters module
  // rootState, rootGetters is the state and getters of root store

  // If the module is not started
  // Commit, state, dispatch, getters use the corresponding commit, state, dispatch, getters module
  // rootState, rootGetters is the current store state and getters
  // The value of state and rootState and the value of getters and rootGetters are the same
  incrementAction({ commit, state, dispatch, getters, rootState, rootGetters }) {
    setTimeout(() = > ctx.commit('increment'), 1000)}}Copy the code
actions: {
  incrementAction({ commit, dispatch }) {
    // Execute your own commit method
    setTimeout(() = > commit('increment'), 1000)

    // When you set {root: true} in the submodule, it means that you are manipulating the mutations or actions of the root store
    // Parameter one: event name
    // Parameter 2: null is used to hold the space if the parameter is not passed

    // omit indicates that the current store is operated on.
    // Set {root: true} to indicate the mutations or actions that need to be implemented
    // is corresponding mutations or actions in the root store
    commit('increment'.null, {root: true})
    dispatch('incrementAction'.null, {root: true}}})Copy the code

Auxiliary function

Vue3 provides four auxiliary functions including mapState, mapGetters, mapMutation and mapAction

But these four helper functions, by default, can only handle state in root state,

If we want these four helper functions to handle state in the module, we need to encapsulate them accordingly

options api

Methods a

<template>
  <div>
    <h2>count: {{ count }}</h2>
    <h2>doubleCount: {{ doubleCount }}</h2>

    <button @click="increment">increment</button>
    <button @click="incrementAction">incrementAction</button>
  </div>
</template>

<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'

export default {
  name: 'App'.computed: {
    ...mapState({
      count: state= >state.home.count }), ... mapGetters({doubleCount: 'home/doubleCount'})},methods: {
    ...mapMutations({
      increment: 'home/increment'
    }),
    ...mapActions({
      incrementAction: 'home/incrementAction'}}})</script>
Copy the code

Way 2

<template>
  <div>
    <h2>count: {{ count }}</h2>
    <h2>doubleCount: {{ doubleCount }}</h2>

    <button @click="increment">increment</button>
    <button @click="incrementAction">incrementAction</button>
  </div>
</template>

<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'

export default {
  name: 'App'.computed: {
    // The second argument to these helper functions can also be written as an object. mapState('home'['count']),
    ...mapGetters('home'['doubleCount'])},methods: {
    ...mapMutations('home'['increment']),
    ...mapActions('home'['incrementAction'])}}</script>
Copy the code

Methods three

<template>
  <div>
    <h2>count: {{ count }}</h2>
    <h2>doubleCount: {{ doubleCount }}</h2>

    <button @click="increment">increment</button>
    <button @click="incrementAction">incrementAction</button>
  </div>
</template>

<script>
// With this helper function, you can generate helper functions belonging to the module
import { createNamespacedHelpers } from 'vuex'

const { mapState, mapGetters, mapMutations, mapActions } = createNamespacedHelpers('home')

export default {
  name: 'App'.computed: {
    // The parameters of these auxiliary functions can also be written as objects. mapState(['count']),
    ...mapGetters(['doubleCount'])},methods: {
    ...mapMutations(['increment']),
    ...mapActions(['incrementAction'])}}</script>
Copy the code

composition api

The useState and useGetters helper functions cannot be used directly in the Composition API, requiring secondary encapsulation of their corresponding content

useMapper.js

import { useStore } from 'vuex'
import { computed } from 'vue'

export default function(mapper, mapFn) {
    const store = useStore()
    const storeState = {}

    const mapStateFns = mapFn(mapper)

    Object.keys(mapStateFns).forEach(key= > {
      storeState[key] = computed(mapStateFns[key].bind({ $store: store }))
    })

    return storeState
}
Copy the code

useGetters.js

import useMapper from './useMapper'
import { mapGetters, createNamespacedHelpers } from 'vuex'

export default function(nameOrMapper, mapper) {
  let mapperFn = mapGetters
  let mapperObj = mapper

  if (typeof nameOrMapper === 'string' && nameOrMapper.length) {
    mapperFn = createNamespacedHelpers(nameOrMapper).mapGetters
  } else {
    mapperObj = nameOrMapper
  }

  return useMapper(mapperObj, mapperFn)
}
Copy the code

useState.js

import useMapper from './useMapper'
import { mapState, createNamespacedHelpers } from 'vuex'

export default function(nameOrMapper, mapper) {
  let mapperFn = mapState
  let mapperObj = mapper

  if (typeof nameOrMapper === 'string' && nameOrMapper.length) {
    mapperFn = createNamespacedHelpers(nameOrMapper).mapState
  } else {
    mapperObj = nameOrMapper
  }

  return useMapper(mapperObj, mapperFn)
}
Copy the code

index.js

import useGetters from './useGetters'
import useState from './useState'

export {
  useGetters,
  useState
}
Copy the code

The user

<template>
  <div>
    <h2>count: {{ count }}</h2>
    <h2>doubleCount: {{ doubleCount }}</h2>

    <button @click="increment">increment</button>
    <button @click="incrementAction">incrementAction</button>
  </div>
</template>

<script>
import { createNamespacedHelpers } from 'vuex'
import { useState, useGetters } from './hooks'

const { mapActions, mapMutations } = createNamespacedHelpers('home')

export default {
  name: 'App'.setup() {
    return {
      ...useState('home'['count']),
      ...useGetters('home'['doubleCount']),
      ...mapActions(['incrementAction']),
      ...mapMutations(['increment'])}}}</script>
Copy the code