How to create a Kirby 3 plugin with Vuex?

plugin

#1

I’m using Vuex in my Kirby plugin because I have a complex state that needs to be managed. I noticed, however, that I’m getting a strange issue where an object I’m creating in a mutation is not reactive.

I couldn’t reproduce the issue in jsfiddle with exactly the same setup, so I tested my plugin outside the panel.plugin() registration. It works as expected. Therefore, something gets messed up when the plugin is loaded on top of the Kirby plugin app.

Here’s an overview of what I have:


index.js

import App from './App.vue'

panel.plugin('oblik/memsource', {
  views: {
    memsource: {
      label: 'Memsource',
      icon: 'globe',
      component: App
    }
  }
})

App.vue

<template>
  ...
</template>

<script>
import store from './store'
export default {
  store,
  ...
}
</script>

store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  ...
})

I can already see a problem with my setup, but I thought it was OK because it has worked so far. In store.js, I import the Vue and Vuex objects and export a Store created with them. That store is using a fresh Vue instance (the one I just imported), instead of using the one that Kirby uses. I guess that’s where the problem stems from.

Another thing is that the index.js file of my plugin contains a whole different Vue build (since I import it in store.js) and basically, there are two instances of Vue running on the panel. This is definitely not good.

Apparently, I need to call use(Vuex) on the Kirby Vue object, not a fresh one that I import. But how? Perhaps Kirby should ship with Vuex ready for usage, even if it doesn’t use it for itself?


#2

But Kirby uses Vuex itself in store.js? Not that I can contribute much here, still new to this Vue stuff.


#3

Yep, I was just about to edit my comment. It didn’t pop in Vue DevTools, but upon inspecting the vendors bundle of Kirby, I saw Vuex there.

Well, if it exists, how do I use it? How do I get a reference to Vuex, so I can call Vuex.Store()? :thinking: I think it would be unreachable in Kirby’s vendors bundle.


#4

I recently had to deal with something similar for a plugin and as far as I can tell there is no easy (clean) way. But I just did a small test and something like this seems to work.

panel.plugin('namespace/plugin', {
  use: [
    function (Vue) {
      Vue.nextTick(() => {
        window.panel.app.$store.registerModule('test', {
          namespaced: true,
          state: {
            value: null
          },
          mutations: {
            SET_VALUE(state, value) {
              state.value = value;
            },
          },
          actions: {
            increment({ state, commit }) {
              const value = state.value + 1;
              commit('SET_VALUE', value);
            }
          }
        });
      });

      setInterval(() => {
        window.panel.app.$store.dispatch('test/increment');
      }, 1000);
    }
  ]
});

#5

Yep, I was also thinking of using registerModule(), but my store uses modules itself, so I had to hack it in another way. That’s the dirtiest thing I’ve ever done in my dev life…

In your App.vue, you specify the $store in Vue’s beforeCreate hook, before the reactivity is initialized. And to create the store, you simply get a reference to Vuex via the component’s $root, which is the Kirby app, then access constructor, which is Kirby’s Vue object, then access that object’s registered plugins, and finally use the 4-th entry there, because it happens to be Vuex:

<template>
  ...
</template>

<script>
export default {
  beforeCreate () {
    var Vuex = this.$root.constructor._installedPlugins[4]
    this.$store = require('./store')(Vuex)
  }
}
</script>

The main downside to this is that since I have to require() the store dynamically, it and all of its dependencies can’t use ES6 imports, which kind of sucks.

But using this hack, everything seems to function normally and I don’t have the reactivity problem I described above. What we need is for Kirby to expose the Vue and Vuex objects in the window so that plugins can use them. I’ve described that here as an idea. It would be blissful if the devs scheduled this for the next Kirby update.