Moiva.io logo

Moiva.io Blog

← all posts

The missing migration guide: from Vue CLI to Vite

Introduction

Moiva.io’s npm dependencies had not been updated for a while and I thought the time has come.

The update didn’t go well though. So I came up with a “workaroud”.

I thought it was a perfect time to try out Vite and see if it can simplify the setup and fix the issue with dependencies conflicts.

I looked for an official guide how to do it and, surprisingly, I didn’t find one. There is a guide how to bootstrap a new project with Vite, but no guidance for existing projects.

Hence I did the migration my own way and I thought it is worth sharing my approach and help others interested in migrating their apps to Vite.

The migration process might vary depending on the tech stack of your project and the way it is built. Hence you might need to adjust the steps to your needs.

My tech stack before the migration: Vue3, Vue CLI, Webpack, Babel, core-js, ESlint, Tailwind, Typescript.

Before diving into the migration steps, let’s understand first what Vite is and what benefits it brings.

What is Vite

The JavaScript landscape of build tools undergoes a significant change. We see a rise of new tools experimenting with new approaches. Some of them quickly become popular.

One of such tools which really stands out is Vite which got 9.4K stars in Q1 2021 (that is 68% growth). Compare that number with 1.2K stars that Webpack got in the same period.

Moiva.io screenshot of Vite monthly downloads and GitHub stars charts

Vite’s npm downloads and GitHub stars statistics. Source: Moiva.io

It is worth mentioning that Vite is not tied to VueJS. It is a framework-agnostic tool and there are people who already use it in their React projects.

Vite essentially does 2 things:

Benefits from migrating to Vite

Vite was created to achieve fast development feedback loop and it clearly succeeded in it:

There are other substantial side benefits as well.

Let’s look now into how we can start using Vite.

Step 1 - Generate a new project with Vite

In the absence of the guide, we need to start somewhere.

Vite provides a command (npm package) to bootstrap new projects. Hence I thought bootstrapping a new project and mimicking the setup would work for me.

I ran npm init @vitejs/app command and followed the prompts.

The resulted file structure looked clear and simple.

A list of files of the Vite's generated project

A list of files generated by Vite

Step 2 - Install required dependencies

I looked into the created package.json file to find out what dependencies I need to install.

{
  "name": "new-vite-project",
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc --noEmit && vite build",
    "serve": "vite preview"
  },
  "dependencies": {
    "vue": "^3.0.5"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^1.2.1",
    "@vue/compiler-sfc": "^3.0.5",
    "typescript": "^4.1.3",
    "vite": "^2.1.5",
    "vue-tsc": "^0.0.24"
  }
}
Vite’s generated package.json file

That file told me that besides vite I need to install the following packages:

I installed the required dependencies in my project.

Step 3 - Add Vite configuration file

I copied the generated vite.config.ts to my project.

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()]
})
Vite’s generated vite.config.ts file

The only adjustment I did there was adding the @ alias (similar to Webpack aliases). I needed that change because I use @ alias in import statements in my app.

export default defineConfig({
  resolve: { alias: { '@': '/src' } },
  plugins: [vue()],
});
vite.config.ts file with my changes

Step 4 - Move index.html to the root folder

Vite treats index.html as the entry point to your application and it should be put in the root folder.

Hence I moved my index.html from /public to / folder.

The only change to the index.html I made was specifying a link to the entry point of my code:

  <script type="module" src="/src/main.ts"></script>
</body>
My changes to index.html file

Step 5 - Adjust tailwind.config.ts

Migration to Vite broke the styling of my application. It worked perfectly fine in the development environment, but the styling was partly broken in production builds.

I figured out soon that it was caused by the location change of the index.html file - Tailwind stopped considering that file while collecting the classes in use. Hence style definitions for classes from that file were not included in the production build.

The problem was fixed easily by tweaking the tailwind.config.js file and specifying the correct path to the index.html file.

module.exports = {
  purge: {
    content: ['./index.html', './src/**/*.vue'],
  },
  theme: {...},
  variants: {},
  plugins: [],
};

tailwind.config.js

Step 6 - Adjust tsconfig.ts

I looked into the generated tsconfig.json file

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "lib": ["esnext", "dom"],
    "types": ["vite/client"]
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
Vite’s generated tsconfig.json file

and adjusted my tsconfig.json file accordingly.

Step 7 - Adjust npm tasks

I used the generated package.json to adjust my npm tasks to build, preview and start the server:

"scripts": {
  "dev": "vite",
  "build": "vue-tsc --noEmit && vite build",
  "serve": "vite preview"
}
Npm tasks from the Vite’s generated package.json file

"build" task includes vue-tsc --moEmit command to type check the code.

I also had to adjust the lint task and run eslint directly instead of relying on Vue CLI: "lint": "vue-cli-service lint" became "lint": "eslint --ext .ts,.js,.vue".

Having done that, I accomplished “feature parity” with the previous setup - the app could be developed, built and deployed.

I just needed to do one small but important thing.

Final Step - Cleanup

We are almost there. The only thing left is to remove the dependencies which are no longer needed.

I removed Vue CLI and its plugins, and core-js. The result was impressive: package-lock.json file became lighter by ~35k lines.

A screenshot from my pull request showing the amount of changes made to package-lock.file

A screenshot from my pull request with the number of changes to package-lock.json file

I also removed babel.config.js and vue.config.js files, they became obsolete.

Conclusion

At first, I didn’t believe in the success of the endeavor.

Vite is still a very young project (1-year-old) and to replace such a huge chunk of the development setup (Webpack and Babel with their plugins/loaders) seemed to be impossible to achieve.

Turned out the migration went very easy, almost seamless.

As a result, I got huge improvements in the development process, fewer dependencies to care about, decreased deployment time (from 2 min to 1 min), and slightly smaller production bundles.

Evan You, the author of Vite and VueJS, did amazing work there. I can’t stop being impressed by what he is doing. Always mind-blowing!


← all posts

Subscribe to the monthly newsletter

Made with by Alexey Antipov
Moiva.io Catalog Blog
GitHub Twitter