Separating vuex stores for dynamically created components

时间秒杀一切 提交于 2019-12-02 19:00:44

For the first operation of NEW button - generating components - we add mutation to our store.js

 mutations: {
    addJob (state) {
      state.jobs.push(state.jobs.length + 1)
...
}

Second, creating local modules. Here we're going to use reusableModule to generated multiple instances of a module. That module we keep in separate file for convinience. Also, note use of function for declaring module state.

const state = () => {
  return {
    count: 0
  }
}

const getters = {
  count: (state) => state.count
}

const mutations = {
  updateCountPlus (state) {
    state.count++
  }
}

export default {
  state,
  getters,
  mutations
}

To use reusableModule we import it and apply dynamic module registration.

store.js

import module from './reusableModule'

const {state: stateModule, getters, mutations} = module

export const store = new Vuex.Store({
  state: {
    jobs: []
  },
  mutations: {
    addJob (state) {
      state.jobs.push(state.jobs.length + 1)
      store.registerModule(`module${state.jobs.length}`, {
        state: stateModule,
        getters,
        mutations,
        namespaced: true // making our module reusable
      })
    }
  }
})

After, we're going to link Hello.vue with its storage. We may need state, getters, mutations, actions from vuex. To access storage we need to create our getters. Same with mutations.

Home.vue

<script>

export default {
  props: ['id'],
  computed: {
     count () {
        return this.$store.getters[`module${this.id}/count`]
     }
  },
  methods: {
    updateCountPlus () {
        this.$store.commit(`module${this.id}/updateCountPlus`)
     } 
  }
}
</script>

Imagine we have lots of getters, mutations and actions. Why not use {mapGetters} or {mapMutations}? When we have several modules and we know the path to module needed, we can do it. Unfortunately, we do not have access to module name.

The code is run when the component's module is executed (when your app is booting), not when the component is created. So these helpers can only be used if you know the module name ahead of time.

There is little help here. We can separate our getters and mutations and then import them as an object and keep it clean.

<script>
import computed from '../store/moduleGetters'
import methods from '../store/moduleMutations'

export default {
  props: ['id'],
  computed,
  methods
}
</script>

Returning to App component. We have to commit our mutation and also let's create some getter for App. To show how can we access data located into modules.

store.js

export const store = new Vuex.Store({
  state: {
    jobs: []
  },
  getters: {
    jobs: state => state.jobs,
    sumAll (state, getters) {
      let s = 0
      for (let i = 1; i <= state.jobs.length; i++) {
        s += getters[`module${i}/count`]
      }
      return s
    }
  } 
...

Finishing code in App component

<script>
import Hello from './components/Hello'
import {mapMutations, mapGetters} from 'vuex'

    export default {
      components: {
        Hello
      },
      computed: {
        ...mapGetters([
          'jobs',
          'sumAll'
        ])
      },
      methods: {
        ...mapMutations([
          'addJob'
        ])
      }
    }
    </script>

Hi and thank you for posting your question and your solution.

I started learning Vuex couple days ago and came across a similar problem. I've checked your solution and came up with mine which doesn't require registering new modules. I find it to be quite an overkill and to be honest I don't understand why you do it. There is always a possibility I've misunderstood the problem.

I've created a copy of your markup with a few differences for clarity and demonstration purposes.

I've got:

  1. JobList.vue - main custom component
  2. Job.vue - job-list child custom component
  3. jobs.js - vuex store module file

JobList.vue (which is responsible for wrapping the job(s) list items)

<template>
    <div>
        <job v-for="(job, index) in jobs" :data="job" :key="job.id"></job>

        <h3>Create New Job</h3>
        <form @submit.prevent="addJob">
            <input type="text" v-model="newJobName" required>
            <button type="submit">Add Job</button>
        </form>
    </div>
</template>

<script>
    import store from '../store/index'
    import job from './job';

    export default {
        components: { job },
        data() {
            return {
                newJobName: ''
            };
        },
        computed: {
            jobs() {
                return store.state.jobs.jobs;
            }
        },
        methods: {
            addJob() {
                store.dispatch('newJob', this.newJobName);
            }
        }
    }
</script>

The Job

<template>
    <div>
        <h5>Id: {{ data.id }}</h5>
        <h4>{{ data.name }}</h4>
        <p>{{ data.active}}</p>
        <button type="button" @click="toggleJobState">Toggle</button>
        <hr>
    </div>
</template>

<script>

    import store from '../store/index'

    export default {
        props: ['data'],
        methods: {
            toggleJobState() {
                store.dispatch('toggleJobState', this.data.id);
            }
        }
    }

</script>

And finally the jobs.js Vuex module file:

export default {
    state: {
        jobs: [
            {
                id: 1,
                name: 'light',
                active: false
            },
            {
                id: 2,
                name: 'medium',
                active: false
            },
            {
                id: 3,
                name: 'heavy',
                active: false
            }
        ]
    },

    actions: { //methods
        newJob(context, jobName) {
            context.state.jobs.push({
                id: context.getters.newJobId,
                name: jobName,
                active: false
            });
        },
        toggleJobState(context, id) {
            context.state.jobs.forEach((job) => {
                if(job.id === id) { job.active = !job.active; }
            })
        }
    },

    getters: { //computed properties
        newJobId(state) { return state.jobs.length + 1; }
    }
}

It's possible to add new jobs to the store and as the "active" property suggest, you can control every single individual job without the need for a new custom vuex module.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!