Creating reusable components with Vue.js - Part 1 - Tooling overview

comments

I decided to learn Vue.js and it's way to implement reusable components. Here I'll start series of blog posts covering my experience. I'll explore the tooling and configuration options for new Vue.js applications.

Other posts in the series

  1. Creating reusable components with Vue.js - Part 1 - Tooling overview This post
  2. Creating reusable components with Vue.js - Part 2 - Viewing Github Issue Comments

Table of Contents

Why Vue.js ?

Actually, I use React.js in my work on daily basis. It is good library for views, but I'd like to touch with something new, to broaden horizons. If you'll read comparison with react on main vue.js site, you'll see claims that Vue offers CLI project generator for easy start a new project. I'd like to explore the tooling convenience too.

Vue.js tooling

I used Jetbrains WebStorm as main IDE. It supports Vue.js single-file components with syntax highlighting and navigation out of the box. Good experience.

I started to use vue-cli for building. Simple "Hello World" with hot-module reloading was as easy as

vue init webpack HelloWorld
cd HelloWorld
npm run dev

Actually, I was hoping to use vue-cli's vue build command for building components. But when I started vue build Component.vue command, I saw on the console warning:

We are slimming down vue-cli to optimize the initial installation by removing the `vue build` command.
Check out Poi (https://poi.js.org/#/home) which offers the same functionality!

Then I decided to evaluate poi.js. It turned out that it is convenient tool for fast prototyping of components. It is config-free (almost), no more configuration of scaffolded webpack needed, you'll just target poi to source file and got the result: it implements full pipeline using webpack with hot module reloading:

# use global instaleld poi (npm i poi -g)
poi index.js

# use local installed poi (npm i poi --save-dev)
node node_modules/.bin/poi index.js

Let's create hello-world Vue.js component and play with this tooling.

Prototyping HelloWorld

I have global installed poi and will use it in examples below. You can install it globally with npm i poi -g command.

Let's create new empty folder for our playground and fill it:

mkdir vue-playground
cd vue-playground
npm init -y
npm install vue --save
mkdir src
touch src/index.js

Let's fill src/index.js file with following content:

// use full vue.js here, with template compiler included:
import Vue from 'vue/dist/vue'
window.vm = new Vue({
  el: '#app',
  template: '<div>Hello, world!</div>',
})

You can use full or light version of Vue.js. The difference between them is that full version has vue template compiler included ant light version is not. Here I use string template property. Therefore Vue.js needs to compile it.

I used import Vue from 'vue/dist/vue' for full version of Vue.js. If you interested in light version of Vue.js, use import Vue from 'vue' instead.

Running HelloWorld application

Let's run poi src/index.js command to start the application. This command runs poi.js in dev mode which bundled with hot-reloading functionality.

By default poi.js extracts all npm dependencies into vendor.js bundle. client.js will contains all your code. The final console output is:

      Asset       Size  Chunks                    Chunk Names
  vendor.js    1.68 MB       0  [emitted]  [big]  vendor
  client.js    2.53 kB       1  [emitted]         client
manifest.js    31.2 kB       2  [emitted]         manifest
 index.html  586 bytes          [emitted]

> Open http://localhost:4000 (copied!)
> On Your Network: http://192.168.0.105:4000

When you ready to deploy your application, run poi build src/index.js. This command will build bundles in production mode, without developer dependencies, and outcome will much less in size:

                   Asset       Size  Chunks             Chunk Names
      vendor.74b45644.js     109 kB       0  [emitted]  vendor
      client.18a0eefa.js  267 bytes       1  [emitted]  client
    manifest.0a75608b.js    1.48 kB       2  [emitted]  manifest
  vendor.74b45644.js.map     773 kB       0  [emitted]  vendor
  client.18a0eefa.js.map     2.3 kB       1  [emitted]  client
manifest.0a75608b.js.map    14.2 kB       2  [emitted]  manifest
              index.html  551 bytes          [emitted]

Other available commands are poi watch for running poi in watch mode and poi test for running tests.

You can redefine mode with --mode argument. Only development, production and test values are supported out of the box.

Exploring bundles with webpack-bundle-analyser

One day you'll want to explore dependencies in the bundle, realize what really contains inside the bundle and probably optimize it. webpack-bundle-analyser package to the rescue!

You can use webpack-bundle-analyser once when you need to explore bundles and then comment it out. To start using that package, install it and provide it as plugin in webpack configuration. poi.js claimed "config-free" but it uses webpack under the hood and allows customize it's webpack configuration with poi.config.js file.

Let's customize it!

Configure poi.js

You can customize poi.js's default webpack template with poi.config.js file. Create it in root folder to start.

I'd like to customize following:

  • do not extract vendor part
    • you can disable code splitting using vendor: false
  • change file name of final bundle
    • you can change file names patterns using filename object
  • do not bundle Vue.js (I'd like to load it from CDN)
    • you can use webpack's externals section
  • use Vue.js as external dependency in index.html
  • use commented-out webpack-bundle-analyser. I could uncomment it once I'll need it

There is two ways for configuring webpack in poi.js.

The first is to specify your webpack properties in plain JSON object for static configuration. Poi will take into account your setting by merging them with the default settings for current mode. There is webpack-chain package which provides fluent declarative interface to configure webpack. Actually, poi.js itself uses webpack-chain package under the hood.

The second way is to specify webpack function which receives config argument which is plain JSON object with webpack configuration you can modify.

Here is sample poi.config.js file which fulfills the requirements:

// poi.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin


module.exports = {
  vendor: false,
  filename: {
    js: 'hello-world.js',
    css: 'style.css',
    images: 'assets/images/[name].[ext]',
    fonts: 'assets/fonts/[name].[ext]',
    chunk: '[id].chunk.js'
  },
  webpack: {
    externals: {
      'vue/dist/vue': 'Vue'
    },
    //plugins: [ new BundleAnalyzerPlugin() ]
  }
}

;
// poi.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin


module.exports = {
  vendor: false,
  filename: {
    js: 'hello-world.js',
    css: 'style.css',
    images: 'assets/images/[name].[ext]',
    fonts: 'assets/fonts/[name].[ext]',
    chunk: '[id].chunk.js'
  },
  webpack(config) {
    config.externals = {
      ...config.externals,
      'vue/dist/vue': 'Vue',
    }
    //config.plugins.push(new BundleAnalyzerPlugin())
    return config // <-- Important, must return it
  }
}
// poi.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const Config = require("webpack-chain")

module.exports = {
  vendor: false,
  filename: {
    js: 'hello-world.js',
    css: 'style.css',
    images: 'assets/images/[name].[ext]',
    fonts: 'assets/fonts/[name].[ext]',
    chunk: '[id].chunk.js'
  },
  webpack: new Config()
    .externals({
      'vue/dist/vue': 'Vue',
    })
    //.plugin('webpack-bundle-analyzer').use(BundleAnalyzerPlugin).end()
    .toConfig()
}

;

And here is index.ejs with external Vue.js dependency specified:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>vue-demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  </head>
  <body>
    <div id="app"></div>
    <noscript>Turn on javascript to show application</noscript>
    <!-- built files will be auto injected -->
  </body>
</html>

The output after poi build src/index.js after such poi.config.js settings customization is:

             Asset       Size  Chunks             Chunk Names
    hello-world.js  732 bytes       0  [emitted]  client
hello-world.js.map    8.08 kB       0  [emitted]  client
        index.html  338 bytes          [emitted]

So, there is no unnecessary packages in the hello-world.js bundle included. Now I can use cdn version of vue.js on my pages and keep my bundles small.

Conclusion

You should know your instrument to be productive. In this post I've described tooling I found best suited for starting fast prototyping vue.js applications.

It turns out that poi.js supports config-free (almost) development out of the box. Actually, it supports not only Vue.js, but React and Angular frameworks too. Worthy of attention tool.

In next post I'll start to implement reusable Vue.js components.

Happy coding!

Comments