Faster hot reloading for Vue development with Vite

Writing JavaScript code in the form of ES6 modules has become a common industry practice. Browsers today already have the support for ES6 modules, but just like with most other problems in web development, not all browsers support it.

So, instead of serving the exact ES modules to the client, we have to rely on a build system to transpile and bundle the code into something that all browsers can process, not just for production but also for development.

This seems like an awful lot of unnecessary bundling during the development cycle, since the developers can just use a modern browser while they’re developing the app.

Would it be nice if we had a way to serve the ES modules directly only for development, and we can let the build system bundle the code for production as usual? That way we get all the speed benefits of running the modules directly, without dropping support for IE.

Vite can help us to do just that.

Vite is a Vue.js application build tool, authored by the creator of Vue, Evan You. In the rest of this article, we’ll take a closer look at what it is and what it does, with a brief hands-on code demonstration.


Modules in a Nutshell

To get a feel of what a modern browser is capable of, let’s start with a simple experiment to run ES modules directly in the browser, all without the help of any build system.

First, a module like this:

📄hello.js

function hello() {
	alert("Hello");
}

export default hello;

And then, a second module importing the above module:

📄main.js

import hello from './hello'

hello();

Finally, a script tag in your HTML connecting to the second module:

<script type="module" src="main.js"></script>

This code setup will work on most modern browsers, except IE. (But, we still need to serve the files using a static server, because importing a module using the <script> tag is subject to CORS restriction.)

The Problem

Since IE still has a sizable share of the market, it’s not practical to just serve

the “future” code and ignore IE. That’s why the current standard workflow is to convert the “future” code into something more “traditional” that all browsers can understand. That’s usually taken care of by tools like Webpack and Babel.

During the development cycle, we have to change and save the code a few hundred times on a daily basis. The hot reloading process involves putting a module through the bundling pipeline every time we change the code in the module, and this is as slow as it sounds.


Now Enter Vite

Vite is an alternative to the standard Vue CLI that intends to fix this particular speed problem.

So during development, you can continue to write your code in ES modules, and Vite will serve the ES modules directly to your browser. All you need to do is to avoid using a browser that doesn’t support ES modules during development.

Since the build system will skip the bundling process and serve the modules directly to the browser, it’s fast when refreshing the page on code changes.

And for production, it will be the same old way, similar to how Vue CLI handles it. Since not all your clients will be browsing your website with a modern browser, Vite will convert the modules into a build that all browsers can understand.

As you can see, Vite focuses on speed during development, not production (because the production build is usually optimized already).


Trying It Out

Getting started with Vite is simple. We just need to run the vite-app initializer with the npm init command:

npm init vite-app my-app

And then, install the dependencies:

cd my-app

npm install

Finally, run the app:

npm run dev

Go to localhost:3000, and you’ll see the default welcome page.


Inspecting the Code

If you check out the rendered HTML using the Code Inspector in your browser, you will find a script tag importing an ES module.

https://firebasestorage.googleapis.com/v0/b/vue-mastery.appspot.com/o/flamelink%2Fmedia%2FVite1.opt.jpg?alt=media&token=ee0ef062-1982-43a7-b7ff-8737aa92fbcf

As you can see, it fetches a module from src/main.js.

If you follow the trail and inspect what’s inside src/main.js, you’ll find that the code your browser has is more or less the same as the source.

Browser:

https://firebasestorage.googleapis.com/v0/b/vue-mastery.appspot.com/o/flamelink%2Fmedia%2FVite2.opt.jpg?alt=media&token=020b9b7e-574d-41ab-8877-ca7cadfa6a86

Source:

📄src/main.js

import { createApp } from 'vue'
import App from './App.vue'
import './index.css'

createApp(App).mount('#app')

The import paths are slightly different between the two versions, but nonetheless, this proves that Vite serves modules instead of a bundle.

If we dig deeper and inspect what’s inside src/App.vue, we’ll see another module, but this time the code is very different from the source module:

Browser:

https://firebasestorage.googleapis.com/v0/b/vue-mastery.appspot.com/o/flamelink%2Fmedia%2FVite3.opt.jpg?alt=media&token=2df7177b-5a7d-4d52-a7c9-50f434877f1a

Source:

📄

<template>
	<img alt="Vue logo" src="./assets/logo.png" />
	<HelloWorld msg="Hello Vue 3.0 + Vite" />
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
	name: 'App',
	components: {
		HelloWorld
	}
}
</script>

Now it should be clear that Vite doesn’t just serve the source code directly to the browser, it still compiles the source whenever there’s something that doesn’t make sense to the browser. For example, the <template> tags and <script> tags inside a .vue file aren’t valid JavaScript, so Vite will transpile them into actual JS code that the browser can understand.

So basically, Vite still runs the modules through a transpilation process, just not a bundling process.


It’s still just Vue

With Vite, the way you would develop your Vue app is still the same.

For demonstration, let’s create a new module called Counter:

📄src/components/Counter.vue

<template>
	<p>{{ count }}</p>
</template>

<script>

export default {
	name: 'Counter',
	data() {
		return {
			count: 0
		}
	}
}
</script>

And then, add a click event:

📄src/components/Counter.vue

<template>
	<p v-on:click="count++">{{ count }}</p>
</template>

<script>
export default {
	name: 'Counter',
	data() {
		return {
			count: 0
		}
	}
}
</script>

Finally swap it with the default HelloWorld component:

📄src/App.vue

<template>
	<Counter>
</template>

<script>
import Counter from './components/Counter.vue'
export default {
	name: 'App',
	components: {
		Counter
	}
}
</script>

Now you should see a number in the browser, clicking the number will increase its count.


Change of Era

Vite is a special tool in the sense that it highlights a gradual change of era in web development, and in turn, foreshadows some future JavaScript development practices.

Choosing to create a JavaScript app in an NPM setup over a pure front-end setup is no longer an exotic choice. Instead, it’s considered the default standard choice. Since the modules can be served to the browser directly without bundling, debugging these modules can be as straight-forward as debugging traditional JavaScript code.

A natural next phase could be a new class of tools that can serve the native modules to supported browsers conditionally, not just in development but also in production. Then, JavaScript bundling will no longer be the norm. Instead, it will just be an exception only for browsers that don’t support modules.

One of these days, web developers will rejoice in the realization that the future is practically now.

Download the cheatsheets

Save time and energy with our cheat sheets.