Search code examples
javascriptvue.jsvuejs2vue-componentvuex

Is there a way to encapsulate Vuex store inside Vue plugin (its install function)?


  • I have a plugin and this plugin uses Vuex
// plugin.js
import Vuex from "vuex";
import store from "./store.js";

export default {
  install(Vue, options) {
    const storeInstance = new Vuex.Store(store);
    Vue.prototype.$store = storeInstance;
  }
};
  • And in that plugin I import a store object.
// store.js
export default {
  actions: {
    SOME_RANDOM_ACTION({ state, commit }) {
      console.log("some random action");
    }
  }
};

Dispatching actions and using state is fine and works as expected.

But when I add this plugin to another Vue instance that uses vuex, store object re-initializes with new state.

// index.js
import Vue from "vue";
import Vuex from "vuex";
import App from "./App.vue";
import plugin from "./plugin.js";

Vue.use(Vuex);
Vue.use(plugin);

new Vue({
  // WARN when i uncomment this next line of code Vuex gets re-initialized with new object
  // store: new Vuex.Store({ state: { hello: "hix" } }),
  components: {
    App
  }
}).$mount("#app");

When you uncomment store initialization, store that was defined in the plugin is now not available.

Currently, I have these solutions in mind:

  1. Export my plugin store object to index.js main app, and use this store as a module.
  2. Use some other state management.

Is there a way to use Vuex inside my plugin?

https://codesandbox.io/s/vibrant-sanne-67yej?file=/src/main.js:0-371


Solution

  • Vuex plugin uses store option to assign store instance to Vue.prototype.$store, similarly to your own plugin.

    If the intention is to use multiple stores, their names shouldn't collide. The key is to name your store object inside plugin something other than $store

    Vue.prototype.$myPluginStore = storeInstance;
    

    But this still doesn't encapsulate $myPluginStore inside the plugin, as it is accessible within the app.

    // App.vue
    
    computed: {
        appState() {
          return this.$store.state;
        },
        pluginState() {
          return this.$myPluginStore.state; // this is now accessible within the main app
        }
    }
    

    It would be a reasonable solution to allow a store to be used as a module of existing store instead of creating a new store, but only when used within one app and not when used as a plugin for a package.

    The main problem is that default store instance ($store) can make use of Vuex helpers - mapGetters, etc.