从两个页面上的组件注册相同的 VueX 商店模块

Same VueX store module registered from components on two pages

我在使用 VueX 和 Vue-Router 时遇到了一个奇怪的情况,我不太确定如何干净地解决它。

我有一个组件(我们称它为“ComponentWithStore”),它注册了一个命名的商店模块,有点像这样:(商店的实际内容并不重要。显然在这个玩具示例中使用 VueX 是多余的,但这是一个非常复杂的应用程序的简化版本,在其中使用 VueX 是有意义的)

// ComponentWithStore.vue
<script>
import module from './componentStore.js';

export default {
    name: 'ComponentWithStore',
    beforeCreate() {
        this.$store.registerModule(module.name, module);
    },

    beforeDestroy() {
        this.$store.unregisterModule(module.name);
    }
}
</script>

然后我将这个组件放在一个视图(或页面)中,然后关联到一个路由(我们称这个页面为“主页”)。

// Home.vue
<template>
  <div class="home">
    Home
    <ComponentWithStore/>
  </div>
</template>

<script>
import ComponentWithStore from '@/components/ComponentWithStore.vue';

export default {
  name: "Home",
  components: { ComponentWithStore }
};
</script>

到目前为止一切顺利,当我访问 Home 路由时,store 模块被注册,当我离开 Home 路由时,store 模块被清理。

然后假设我创建了一个新视图(页面),我们称它为“关于”,这个新的“关于”页面与 Home.vue 基本相同,因为它也使用了 ComponentWithStore。

// About.vue
<template>
  <div class="about">
    About
    <ComponentWithStore/>
  </div>
</template>

<script>
import ComponentWithStore from '@/components/ComponentWithStore.vue';

export default {
  name: "About",
  components: { ComponentWithStore }
};
</script>

现在我在从主页导航到关于时遇到以下错误:

vuex.esm.js?2f62:709 [vuex] duplicate namespace myComponentStore/ for the namespaced module myComponentStore

发生的情况是“关于”的商店模块已注册“主页”的商店模块未注册之前,因此出现重复命名空间错误。

所以我很清楚问题是什么,但是我不确定解决这种情况的最干净的解决方案是什么。欢迎所有想法

可在此处找到完整示例:https://github.com/mmgagnon/vue-module-router-clash 要使用,只需 运行 它并在主页和关于页面之间切换。

正如您所提到的,问题是由于挂钩的顺序造成的。您只需要使用正确的钩子来确保旧组件在新组件再次注册之前先注销模块。

在较高级别,这是从“主页”导航到“关于”时您所处情况的挂钩顺序:

  1. 关于创建前
  2. 关于创建
  3. 毁灭前的家
  4. 家园被毁
  5. 关于挂载

因此您可以在 mounted 挂钩中注册模块并在 beforeDestroydestroyed 中注销它。

虽然我还没有测试过这个。如果您的组件在创建之后和安装之前需要访问存储,则它可能无法工作。


更好的方法是创建一个抽象来注册和注销允许重叠的模块。

未经测试,但类似这样的方法可能有效:

function RegistrationPlugin(store) {
  const modules = new Map()

  store.registerModuleSafely = function (name, module) {
    const count = modules.get(name) || 0

    if (count === 0) {
      store.registerModule(name, module)
    }

    modules.set(name, count + 1)
  }

  store.unregisterModuleSafely = function (name) {
    const count = modules.get(name) || 0

    if (count === 1) {
      store.unregisterModule(name)
      modules.delete(name)
    } else if (count > 1) {
      modules.set(name, count - 1)
    }
  }
}

创建商店时指定插件:

const store = new Vuex.Store({
  plugins: [RegistrationPlugin]
})

现在像这样注册和取消注册您的模块:

beforeCreate() {
  this.$store.registerModuleSafely(module.name, module)
},

destroyed() {
  this.$store.unregisterModuleSafely(module.name)
}