We'll learn how to make our own plugin for Vue.js, and distribute it on NPM for everyone to use.
Plugins are what makes our lives as developers so much more productive. Most of our projects depend on them as they allow us to ship new features with great speed.
As stated in the Official Vue.js documentation, there is no strictly defined scope for a plugin. It simply adds global-level functionality to your project. But they typically fall into these five categories based on the things we are trying to achieve with them:
- Add some global methods or properties (e.g. this is what Vuex or vue-router does).
- Add one or more global assets (e.g. something like a stylesheet with/or a JavaScript library).
- Add some component options by global mixin (e.g. this is what vue-html-to-paper does).
- Add some Vue instance methods by attaching them to Vue.prototype (e.g. this is what vue-axios does).
- A library that provides an API of its own, while at the same time injecting some combination of the above.
Now that you understand how handy plugins can be and what needs they can fulfill, let’s see how to add one to your project. Then, we’ll learn how to make our own and distribute it on NPM for everyone to use (yes, it’s going to be super fun!).
How to Add a Vue.js Plugin to Your Project.
To use your plugin after you’ve installed it with npm install
(or yarn add
), you need to go to your main.js
file. This is the entry point that drives our Vue application. Import it and call the Vue.use()
global method. One word of caution though: All plugins must instantiated before you start your app with new Vue()
.
import Vue from"vue";import YourPlugin from"yourplugin";
Vue.use(YourPlugin);newVue({// [...]})
There is also another way to add a new plugin when the package author allows it: dropping the CDN link in your header’s script tag.
<script src="https://cdn.jsdelivr.net/npm/yourplugin@latest/dist/yourplugin.min.js"></script>
Sometimes, you would like to customize how a plugin behaves. You can easily do so by passing some options to it when calling Vue.use()
. Here is how it works:
Vue.use(YourPlugin,{
someOption:false,
anotherOption:false})
For instance with vue-chartist, you can choose the text to display when no data is available to properly draw the chart as follows:
Vue.use(VueChartist,{
messageNoData:"You have not enough data"});
Now let’s get back to the main event — building your first Vue.js plugin together.
How to Build Your Own Vue.js Plugin from Scratch
If you are reading this, you are probably a frontend developer like me. And like any other frontend developer, you probably love having nice handsome buttons for your interfaces! So that’s what we’ll be building: a bunch of nice handsome buttons that we’ll be able to reuse. This will save us a lot of time for future projects! You’ll also have the knowledge to package all your remaining base components and why not release your own design system?
Step 1: Initializing the Plugin Structure
Let’s create an empty folder for our package and initialize NPM. This will generate a new package.json
file. We’ll deal with it later.
$ mkdir nice-handsome-button &&cd nice-handsome-button
$ npm init
# The command above will create a new package.json# Press enter to answer all the following questions
Add a new folder called src at the root, in which you create a new file NiceHandsomeButton.vue
. You can rapidly prototype with just a single *.vue
file with the vue serve
and vue build
commands, but they require an additional global addon to be installed first:
npminstall -g @vue/cli
npminstall -g @vue/cli-service-global
Now if you run:
$ vue serve NiceHandsomeButton.vue
Visit http://localhost:8080/
. A blank page should appear in your browser. Let’s work on our button component from now on!
You can read more about @vue/cli-service-global in the official documentation. This addon is that it is quite useful for working on a single
.vue
file without scaffolding an entire project withvue create my-new-project
.
Step 2: Working on Our Handsome Button Component
Template
As this tutorial is not about learning how to write Vue components, I expect you to be familiar with the basics. The full code of our nice handsome button is available below (the template, the JavaScript logic and the style). Copy it, open NiceHandsomeButton.vue
and paste the content inside.
<template><button@click="onClick"@dblclick="onDoubleClick":class="[
'nice-handsome-button',
'nice-handsome-button--' + color,
'nice-handsome-button--' + size,
{
'nice-handsome-button--rounded': rounded
}
]"><slot></slot></button></template>
We have kept things simple, but here are a few things to note:
- I am using BEM. If you are not familiar with it, please read this now: MindBEMding — getting your head 'round BEM syntax.
-
I added the props
color
,size
androunded
. As their names indicate, they will allow us to control the color, the size and whether or not our button should be rounded. -
I’m also using a slot for the content so that we can use it like a normal button
<nice-handsome-button>My Button Label</nice-handsome-button>
.
JavaScript
Let’s define the props our component can accept as well as the two methods that will emit an event when we click/double-click on it.
<script>exportdefault{
props:{
color:{
type: String,default:"blue",validator(x){return["blue","green","red"].indexOf(x)!==-1;}},
rounded:{
type: Boolean,default:true},
size:{
type: String,default:"default",validator(x){return["small","default","large"].indexOf(x)!==-1;}},},
methods:{onClick(event){this.$emit("click", event);},onDoubleClick(event){this.$emit("dblclick", event);},}};</script>
Style
Last but not least, let’s style our component.
<style>
.nice-handsome-button {display: inline-block;outline:0;border:1px solid rgba(0, 0, 0, 0.1);color:#ffffff;font-weight:500;font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;user-select: none;cursor: pointer;}/* --> COLORS <-- */.nice-handsome-button--blue {background-color:#0194ef;}.nice-handsome-button--green {background-color:#1bb934;}.nice-handsome-button--red {background-color:#e1112c;}/* --> SIZES <-- */.nice-handsome-button--small {padding:8px 10px;border-radius:4px;font-size:12px;line-height:12px;}.nice-handsome-button--default {padding:12px 14px;border-radius:6px;font-size:14px;line-height:16px;}.nice-handsome-button--large {padding:16px 18px;border-radius:8px;font-size:16px;line-height:20px;}/* --> BOOLEANS <-- */.nice-handsome-button--rounded {border-radius:60px;}</style>
Our component is now ready to use and can be used like this:
<nice-handsome-button:rounded="true"color="red"size="large">My Button</nice-handsome-button>
Let’s package it now.
Step 3: Write the Install Method
Before we start this section, let’s create an index.js
file in your src folder.
Remember that Vue.use()
global we talked about earlier? Well… what this function does is call the install()
method that we will define now.
This function takes two parameters: the Vue
constructor and the options
object that a user can set. You can skip the last argument if you don’t need it as it is optional. But if you want to make your plugin customizable, this is where you will catch the different parameters:
Vue.use({
param:"something"})`.// Then in your install method options.param will equal to "something"
In index.js
, let’s import our component and define our install
method.
import NiceHandsomeButton from"./NiceHandsomeButton.vue";exportdefault{install(Vue, options){// Let's register our component globally// https://vuejs.org/v2/guide/components-registration.html
Vue.component("nice-handsome-button", NiceHandsomeButton);}};
Congratulations, you almost made it!
Step 4: Reworking package.json
Open your package.json
file that you created when running npm init
.
{"private":false,"name":"nice-handsome-button","version":"0.0.1","description":"A nice handsome button you will love","author":"Nada Rifki","license":"MIT","main":"./dist/index.cjs.js","scripts":{"dev":"vue serve NiceHandsomeButton.vue","build":"bili --name index --plugin vue --vue.css false"},"files":["dist/*"]}
A few notes:
private
is set tofalse
. This means your package is public (i.e. everyone is able to see and install it).-
Choose a
name
for your package. You have to make sure that it’s not already taken. -
The version number is set to
0.0.1
. You will have to increment this number every time you publish an update for your package. If you are not familiar with semantic versioning, I highly recommend you read this. - Choose a description that describes your package in a few words. This will help other developers understand what pain your plugin solves.
-
The
main
is the primary entry point to your program. That is, if your package is namedfoo
, and a user installs it, and then doesrequire("foo")
, then your main module’s exports object will be returned. -
The
scripts
property is a dictionary containing script commands that you can easily run withnpm run
. -
The
files
property specifies which files should be published on NPM. It is usually a bad idea to publish everything. We’ll be usingbili
, so all files indist
folder should be included.
You can read more about all these properties in the official NPM documentation.
Bundling Your Library
In case you don’t know, bundling is the process of grouping all your code from all your files in your project into one single file. The reason behind is simply to increase performance. This will also minify the code and do some other cool things.
To do so, we’ll use Bili, a fast and zero-config library bundler that uses Rollup.js under the hood.
Let’s install it.
$ npminstall --save-dev bili
# We'll need these two packages to transpile .vue files# https://bili.egoist.moe/#/recipes/vue-component
$ npminstall --save-dev rollup-plugin-vue
$ npminstall --save-dev vue-template-compiler
Now, create our bili.config.js
file in the root folder and add our bundling settings:
module.exports ={
banner:true,
output:{
extractCSS:false,},
plugins:{
vue:{
css:true}}};
All you have left to do is run the command below on your terminal and your package is bundled — it’s as easy as 1-2-3!
$ npx bili
You should obtain a new dist
folder with a index.cjs.js
file.
By default
<style>
tag in Vue SFC will be extracted to the same location where the JS is generated but with.css
extension. That’s why we added--vue.css false
in the command above.
To learn more about Bili and how to customize it, I recommend you take a look at the documentation.
Sharing Your Wonder on NPM
Now that your package is ready, the only thing left for you is to publish your package on NPM.
Start by creating an account on NPM (you can also run npm adduser
if you prefer using the command lines). Then go to your terminal and run npm login
. You will have to input your username, password and email.
You can check that you are logged in by typing npm whoami
. This should display your username.
There is now only one terminal command that stands between you and publishing your package:
$ npm publish
And voilà!
To update your package, just increment the version
number in your package.json
and rerun npm publish
.
How to Use Your Newly Published Library
You can install it like any other package:
$ npminstall --save nice-handsome-button
In your main.js
, or a similar entry point for your app:
import NiceHandsomeButton from"nice-handsome-button";import Vue from"vue";
Vue.use(NiceHandsomeButton);
Now, the nice handsome button should be able in any of your .vue
files.
<nice-handsome-button:rounded="true"color="red"size="large">My Button</nice-handsome-button>
Where to Go from There?
There is a lot you can do now and that’s awesome! You learned how to package your first component and publish it on NPM for everyone to use. But don’t stop now! Here are a few ideas that may inspire you:
-
Improving this button component by allowing people to set an icon on the left, managing other events like
mouseenter
ormouseout
and so on. - Adding new components to this one and releasing a design system.
- Building a different plugin like a directive or a mixin.
Easy peasy! Finally, we’re done. You can find the plugin’s final code on my GitHub. Feel free to give me your feedback or to reach me on Twitter @RifkiNada if you need help. Enjoy and have a good day!