Skip to content

2. Structure of a Vue Plugin

Fatih Kadir Akın edited this page Aug 5, 2019 · 5 revisions

If you don't created a package yet, please read the creating a Vue Plugin page.

Development of the Plugin

Now we have a plugin folder structure, only the important files were shown below:

package.json
src
|____types
| |____index.d.ts             # Type definitions for TypeScript
|____utils.js                 # Some utility functions for your plugin
|____vue-todo-component.vue   # A sample Custom Component, you can remove it if you don't want to provide one.
|____vue-todo.js              # The main file of your plugin.
.storybook
|____stories.js               # Your plugin's Storybook stories.
examples
|____kitchensink              # An example folder for your plugin, you can duplicate
| |____App.vue
| |____index.js

Commands

Command Description
yarn build Builds your project for production usage
yarn example:kitchensink Runs the examples/kitchensink application on http://localhost:4000
yarn storybook Runs storybook stories on http://localhost:{a free port}

vue-todo.js

This is the main file of your plugin, this has some preset abilities like installing itself, registering components, directives, Vuex stores, instance variables or some mixins.

You can see all the file by opening it but we'll focus on the following part of the file:

import VueTodoComponent from './vue-todo-component.vue';

export default class VueTodo {
  constructor(options = {}) {
    const defaults = {
      // This is your plugin's options. It will be accessible with this.options
      accessorName: '$todo'
    };
    this.options = { ...defaults, ...options };
  }

  // Some instance methods that you can access from this.$myPlugin
  world() {
    return 'world';
  }

  static register = (Vue, options, store) => {
    console.log('Here is the options of the component', options);
    console.log('Here is the store of the app', store);
    // You can use `this.options` property to access options.

    // Delete this line if your plug-in doesn't provide any components
    Vue.component('VueTodo', VueTodoComponent);

    // Vue.directive('your-custom-directive', customDirective);

    // registerVuexStore(store, 'counterStore', {
    //   namespaced: true,
    //   state: { counter: 0 },
    //   getters: {
    //     counter: state => state.counter
    //   },
    //   actions: {
    //     increment: ({ commit }) => commit('increment')
    //   },
    //   mutations: {
    //     increment: state => state.counter++
    //   }
    // });
  };

  // Some lifecycle hooks to add on mixin
  static mixin = () => ({
    mounted() {
      console.log('Hey! I am running on every mount, please remove me!');
      console.log(this.$store);
    }
  });

The Skeleton

The code is separated into 5 main parts:

  1. Components
  2. Options
  3. Instance methods/getters
  4. Registrations
  5. Mixins

Since the plugin is based on a class, you can convert it to your own structure.

// 1. COMPONENTS
// Here is the place for your component imports

import MyButton from './myButton.vue';
import MyInput from './myInput.vue';

export default class VueMyUILibrary {
  constructor(options = {}) {

    // 2. OPTIONS
    const defaults = {
      // This accessorName is required, you can remove it but you'll need to remove this from
      // the logic. Most of the time, plugins need an accessor like that.
      accessorName: '$myUI',
      
      // Here is the place for your another options
    };

    // Setting options with defaults.
    this.options = { ...defaults, ...options };
  }


  // 3. INSTANCE METHODS
  // You may want to have some abilities by reaching from the component using `this.$myUI.{method}`.
  // This may be handy on your plugins.
  doSomething() {
    /* */;
  }

  // 4. REGISTRATIONS
  // This is a static method to register your:
  //   - Components
  //   - Directives
  //   - Vuex Stores
  //   - Mixins
  //   - Global variables/methods.
  static register = (Vue, options, store) => {
    // This method is being called with 3 arguments, the Vue instance, the options and the global Vuex store.
    // You can use `registerVuexStore` utility to register a custom store.

    // e.g. let's register the things we've imported.
    Vue.component('my-button', MyButton);
    Vue.component('my-input', MyInput);
  };

  // 5. THE MIXIN
  // These will be registered to the components. `Vue.mixin` could be used on `register` but I recommend
  // using this since it's `beforeCreate` has some required tasks about registering plugin. 
  static mixin = () => ({
    data: () => ({
      heyIAmAStateOnEveryComponent: true
    }),
    mounted() {
      console.log('Hey! I am running on every mount, please remove me!');
    },
    /* Other things to do */
  });

1. Components

You can have many custom components in your plugin. E.g.

  • Building a Vue UI plugin that provides a UI component set,
  • A functional plugin that provides a component that makes some complex things,

You need to import them in your vue-plugin.js (your-dashed-plugin-name.js)

It is a simple import statement:

import MyButton from './myButton.vue';
import MyInput from './myInput.vue';

2. Options

You may need to allow your users customize something on your plugin. E.g.

  • The theme options if you're building an UI plugin,
  • Simple on/off switches.

You need to add some extra keys to defaults constant since you can only provide defaults. this.options will be set by boilerplate.

Note: accessorName is required on the file, you shouldn't remove it. If you remove it please change references. But I don't recommend making it static, plugin users may need to change it.

4. Registrations

If you're providing a component, directive, Vuex store or something needs to be injected into users app, you need to setup your register static function.

static register = (Vue, options, store) => {
  Vue.component('my-button', MyButton)
  Vue.component('my-input', MyInput)

  Vue.directive('focus', ...)

  Vue.mixin({ ... })
};
Injecting Vuex stores with registerVuexStore

vue-plugin-boilerplate has a registerVuexStore helper that registers a custom store to users' store.

static register = (Vue, options, store) => {
  registerVuexStore(store, 'my-awesome-store', {
    namespaced: true,
    state: { counter: 0 },
    getters: {
      counter: state => state.counter
    },
    actions: {...}
    mutations: {...}
  })
};

You can also import these stores from file:

import vuePluginStore from './vue-plugin-store';

...
static register = (Vue, options, store) => {
  registerVuexStore(store, 'my-awesome-store', vuePluginStore)
};
...

5. The Mixin

The boilerplate has a beforeCreate method to setup Vue instances. You can extend the mixin by using mixin static method of the plugin.

static mixin = () => ({
  data: () => ({
    heyIAmAStateOnEveryComponent: true
  }),
  methods: {
    hello() {
      console.log('I am a method on every component!')
    }
  }
});