为动态创建的组件分离 vuex 存储

Separating vuex stores for dynamically created components

这个问题让我有点卡住了。不幸的是,我在这里找不到答案(询问也没有帮助)。所以在做了一些研究并到处询问之后,似乎我找到了解决这个问题的方法。

If you have a question that you already know the answer to, and you would like to document that knowledge in public so that others (including yourself) can find it later.

当然,我的回答可能不是最理想的,而且我知道不是,这就是我发帖的关键——改进它。

请注意,我没有在示例中使用操作。这个想法是一样的。

让我们从开始说明问题:

假设我们有 App.vue,它动态生成名为 Hello.

的本地组件
<template>
  <div id="app">
    <div>
      <hello v-for="i in jobs" :key="i" :id="i"></hello>
      <button @click="addJob">New</button>
    </div>
  </div>
</template>   

<script>
import Hello from './components/Hello'

export default {
  components: {
    Hello
  }...

store.js

export const store = new Vuex.Store({
  state: {
    jobs: []
  }
})

我们正在使用 v-for 指令通过遍历数组 jobs 来生成组件。截至目前,我们的 store 仅包含带有空数组的 state。 按钮 New 应该做两件事:

1) 创建新组件 Hello,换句话说,将元素添加到 jobs(让它是数字),这将被分配作为 <hello>keyid,并作为 props.

传递给本地组件

2) 生成本地存储 - modules - 以将任何数据限制在新创建的组件范围内。

Hello.vue

<template>
  <div>
    <input type="number" :value="count">
    <button @click="updateCountPlus">+1</button>
  </div>
</template>

export default {
  props: ['id']
}

简单组件 - 输入按钮加 1。

我们的目标是设计这样的东西:

对于 NEW 按钮的第一次操作 - 生成组件 - 我们将 mutation 添加到我们的 store.js

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

其次,创建本地模块。这里我们将使用 reusableModule 来生成一个模块的多个实例。为了方便起见,我们将该模块保存在单独的文件中。此外,注意使用函数来声明模块状态

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

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

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

export default {
  state,
  getters,
  mutations
}

要使用 reusableModule 我们导入它并应用动态模块注册。

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
      })
    }
  }
})

之后,我们将 link Hello.vue 及其存储。我们可能需要 stategettersmutationsactions 来自 vuex。要访问存储,我们需要创建 getters。与 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>

假设我们有很多 gettersmutationsactions。为什么不使用 {mapGetters}{mapMutations}?当我们有多个模块并且知道所需模块的路径时,我们就可以这样做。不幸的是,我们无法访问模块名称。

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.

这里没什么帮助。我们可以将 gettersmutations 分开,然后将它们作为对象导入并保持干净。

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

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

正在返回 App 组件。我们必须提交 mutation 并为 App 创建一些 getter。展示我们如何访问位于模块中的数据。

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
    }
  } 
...

App 组件中的结束代码

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

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

您好,感谢您发布问题和解决方案。

我前几天开始学习Vuex,遇到了类似的问题。我已经检查了您的解决方案并提出了不需要注册新模块的我的解决方案。我觉得这太矫枉过正了,老实说,我不明白你为什么要这样做。总是有可能我误解了这个问题。

为了清晰和演示目的,我创建了一份您的标记的副本,其中有一些差异。

我有:

  1. JobList.vue - 主要自定义组件
  2. Job.vue - 作业列表子自定义组件
  3. jobs.js - vuex 存储模块文件

JobList.vue(负责包装作业列表项)

<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>

工作

<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>

最后 jobs.js Vuex 模块文件:

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; }
    }
}

可以向商店添加新作业,正如 "active" 属性 所建议的,您可以控制每个单独的作业,而无需新的自定义 vuex 模块。