-
Notifications
You must be signed in to change notification settings - Fork 9
Home
This section is about creating the plugin folder and getting ready to create your awesome plugin.
Let's create a todolist plugin. Why not?
git clone --depth 1 https://github.com/f/vue-plugin-boilerplate.git my-todolist-plugin
cd my-todolist-plugin
This will clone the boilerplate code into your directory. But wait, we have a small work to start working on it.
There's a command called press
inside the directory, it simply prepares your plugin.
./press
It will ask some questions about your plugin.
Your plugin name? (with dahshes like vue-plugin-boilerplate):
You should enter a single name or multiple names with dashes. The vue-
prefix is recommended but not necessary. You can edit it later anyway. Let's say it vue-todo
Your plugin name? (with dahshes like vue-plugin-boilerplate): vue-todo
Your plugin class name? (pascal case like VuePlugin):
Now you'll be prompted for the class name it will create. This must be in PascalCase
. Let's say VueTodo
then.
Your plugin class name? (pascal case like VuePlugin): VueTodo
Your plugin options name? (like "yourPlugin" to be used as new Vue({ yourPlugin: {...} }))
When your users configure your plugin, they will need an object key to setup your plugin. What should it be? Let's say it todo
and your users will be able to use your plugin as following:
import Vue from 'vue';
Vue.use(VueTodo)
const app = new Vue({
todo: { /* some settings */ }
})
Looks cool, right? Writing todo
:
Your plugin options name? (like "yourPlugin" to be used as new Vue({ yourPlugin: {...} }))
Your plugin accesor name? (like "helloWorld" to be used as this.$helloWorld):
You want your users to access your plugin instance from their Vue components. We should define an accessor name to make them use your plugin easily like following:
<script>
export default {
mounted() {
this.$todo.add('buy milk')
}
}
</script>
So we should say it todo
. The $
prefix will be added automatically but you can remove it from the source if you don't prefer.
You're building an Open Source plugin (I hope), so you need a repo.
Your plugin's GitHub address? (like "f/vue-plugin-boilerplate"):
Just write the username/repo-name
and the package.json
file will be updated.
The press
command made some changes in the boilerplate files, now you should remove them to start building it.
Thanks God, the press
command gives you the list of the files (with remove commands) to make you start development from scratch. Run the command press
gave you, it should look like following:
Pressing created some leftovers. You can run following commands to remove them now:
rm -rf ./.git
rm -f ./.storybook/stories.js.bak
rm -f ./.storybook/config.js.bak
rm -f ./README.tpl.md.bak
rm -f ./examples/generic/index.js.bak
rm -f ./examples/generic/App.vue.bak
rm -f ./nuxt/index.js.bak
rm -f ./README-BOILERPLATE.md.bak
rm -f ./package.json.bak
rm -f ./src/utils.js.bak
rm -f ./src/types/vue-plugin.d.ts.bak
rm -f ./src/vue-plugin.js.bak
rm -f ./src/vue-plugin-component.vue.bak
rm -rf ./resources
rm -f ./press
git init
yarn install
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
|____generic # An example folder for your plugin, you can duplicate
| |____App.vue
| |____index.js
Command | Description |
---|---|
yarn build |
Builds your project for production usage |
yarn example:generic |
Runs the examples/generic application on http://localhost:4000
|
yarn storybook |
Runs storybook stories on http://localhost:{a free port}
|
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 code is separated into 5 main parts:
- Components
- Options
- Instance methods/getters
- Registrations
- 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 */
});
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';
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.
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({ ... })
};
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)
};
...
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!')
}
}
});