Vue-cli ▶ Vite Migration

ankita srivastava
8 min readNov 22, 2022

--

https://www.google.com/search?q=vue-cli+to+vite+migration&source=lnms&tbm=isch&sa=X&ved=2ahUKEwjW1bqdvML7AhWGu6QKHXf5Ab8Q_AUoA3oECAIQBQ&biw=1787&bih=917&dpr=2#imgrc=YFJPUyoLf-J76M

Yes, this is yet another blog that will tell you about how to migrate vue-cli and webpack to vite, but with a few additional steps. Despite the fact that there are so many blogs available for vite migration, I decided to write this blog because of the challenges I faced while migrating my application to vite. However, if you have a basic Vue application, you can easily follow this blog. My application had a bit different setup than a basic Vue application, which we will be seeing here.

Tech Stack

My current project was built using the Create Vue app, so some of the obvious dependencies like vue-cli, webpack, babel, jest, etc. were already there. Other than this, below are the mentioned technologies:

Migration Guide

When I started following other blogs/documentation for migration, I faced a few challenges as I was also using graphQL and the Apollo client in the project. Other than this, I have a few challenges, which we will resolve here. For the time being, let us begin migration:

Step 1: The first step is to remove any vue-cli dependencies present in your package.json file.

//remove below dependencies
"@vue/cli-plugin-babel": "<any version>",
"@vue/cli-plugin-eslint": "<any version>",
"@vue/cli-plugin-router": "<any version>",
"@vue/cli-plugin-vuex": "<any version>",
"@vue/cli-service": "<any version>",

Step 2: Now, you need to introduce vite in your application by running the below command, which will install vite and the vite vue plugin to your package.json file.

npm install vite @vitejs/plugin-vue

Also, if you’re using Vue2, you should probably install one more dependency, which I skipped because I was using Vue3 This is only required for projects getting migrated from Vue2 ▶️ vite.

npm install vite-plugin-vue2

Step 3: Now that you have vite in your application, you can remove the below-mentioned dependency, as it will be taken care of by the vite plugins.

"vue-template-compiler": "<any version>"   //remove it

Step 4: After installing vite, we must remove any unnecessary dependencies and files. We don’t need the babel.config.js file anymore, so we can delete it. As we don’t need Babel, we will remove the dependencies associated with it and uninstall it as well.

"babel-eslint": "<any version>",   //remove it
"core-js": "<any version", //remove it

Step 5: Now, we will configure vite in our application. To accomplish this, we will create a new file, vite.config.js as shown below:

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { fileURLToPath } from 'url';
import path from 'path';

const filename = fileURLToPath(import.meta.url);
const pathSegments = path.dirname(filename);

export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(pathSegments, './src'),
},
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'],
}
})

This is the basic configuration of the vite config file with a few additional configurations for my project, which I will explain so that you can decide whether to keep it or not for your use case.

  • resolve.alias: I added this config just for a relatively smaller path when I am importing any file into the project. A file that is present at path ./src/folder1/file1 can be imported using @/folder1/file1.
  • resolve.extensions: As mentioned before, I am also using typescript in the project, so I added support for a few extensions.

Now, because I am using GraphQL and the Apollo client, just having the above vite configuration gave me an error mentioning that it could not find React, and I was wondering why even it was looking for React in a Vue application. After doing some research, I found that Apollo Client was internally creating this issue. So, to fix this error, we need to add the below code to our vite config file. This step you might not find in the other vite migration blogs as it’s very specific to the Apollo client, which others might not be using.

optimizeDeps: {
include: ['@apollo/client/core', "@vue/apollo-composable",],
exclude: ['@apollo/client'],
}

Step 6: Before Vite, your index.html file used to get served from public folder but not anymore. Vite looks for your index.html file in your root directory. So, move your index.html file outside public folder for vite to found it. And yes leave your favicon’s and other files there only, no need to move them.

Step 7: Few more changes required in index.html.

a.) Firstly, We need to remove <%= htmlWebpackPlugin.options.title %> from your title tag or anywhere else in your index.html file and replace it with hardcoded String

<title><%= htmlWebpackPlugin.options.title %><title> //Delete it
<title>My Project Title<title> //Add this

b.) Next, we need to remove <%= BASE_URL%> from your link tag and replace it with the actual path, as shown below:

<link rel="icon" href="<%= BASE_URL %>/favicon.ico"> //Delete it
<link rel="icon" href="/favicon.ico"> //Add the hard coded path for favicon

c.) Then, you also need to inject your Javascript application manually, because it’s no longer getting auto injected. For injecting Javascript, add this to your index.html file, where /src/main.ts is the main file to start the application.

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

Step 8: Now, we will update the environment variable in the application. All the environment variables starting with VUE_APP_XXX will be renamed to VITE_APP_XXX for Vite to understand them.

Also, prior to vite migration, we used process.env.VUE_APP_XXX to access environment variables, but we will now replace this with import.meta.env.VITE_APP_XXX throughout the project. You should be able to access all the environment variables using import.meta.env in the application.

Step 9: Now, wherever we are importing vue components, we must include the.vue extension. This is an important step for SFC imports, but I felt it too much and hence escaped by adding .vue extension in the extension list in the vite.config file. But let me inform you that this is NOT recommended. So, please go ahead and add the .vue extension in your vite.config.file for SFC imports.

Step 10: Finally, Webpack provided us with a lot of commented code that was essentially used to build applications in chunks, but that no longer works with Vite, so we can remove all such comments. Vite also has some support for building applications in chunks, which we can discuss some other time, if people are interested.

And yes, the migration is complete, so now ideally, my application should be up and running perfectly fine, but when I tried running it, I found all the icons for my project had disappeared, and also, a lot of functionalities were not rendering. Now the real struggle starts, which took a few days for me to get it resolved.

Let's discuss those problems:

Problem 1: No icons/images are getting displayed for me.

A bit of context here: I have a custom component Icon.vue that uses a vue dynamic component, and we used to pass a prop that is the icon or svg filename, and based on that, it renders the svg file as a vue component. We have an index.ts file that exports all the svg files (around 150 in count) and then gets served by the Icon component.

Index.ts ( exporting 150 svg files)

export { default as image1 } from './image1.svg';
export { default as image2 } from './image2.svg';
export { default as image3 } from './image3.svg';
.
.
.
export { default as image150 } from './image150.svg';

Icon.vue

<template>
<span>
<component :is="variant" />
</span>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import * as icons from '@/icons';

export default defineComponent({
components: icons,
props: {
variant: {
type: String,
required: true,
},
},
});

AnyComponent.vue ( usage of Icon component )

<Icon variant="image2/>

Previously, my svg files were rendered in this manner, but they have all vanished.

Solution: After debugging, I discovered that after vite migration, I was unable to export multiple svg files from index.ts, so I spent a lot of time researching how to export it in vite. Vite provides a glob method to do the same job, which allows us to import multiple modules from the file system. By providing options, you can choose whether to import it as a svg file or a component. I tried the component way, but it didn’t work for me, so I went ahead with the raw svg format. Also, by default, the glob method does lazy loading of the files, but in my case, I want all the files at once, so I used the globEager method. Now, my index.ts got changed and looked like this:

const svgModules = import.meta.globEager('./*.svg', { as: 'raw' });
const formattedSvgModules: { [key: string]: unknown } = {};

Object.keys(svgModules).forEach((filePath) => {
const svg = svgModules[filePath];
const filename = filePath.replace('.svg', '').replace(/[^(A-Za-z0-9)]/gi, '').toLowerCase();
formattedSvgModules[filename] = svg;
});

export default formattedSvgModules;

I just did a little formatting to get a better key: value pair in my object, so you can ignore that if you are fine with objects in this format 😜 :

// Before formatting
{'./image1-abc.svg': '<svg>Actual svg code</svg>' }

// After formatting
{'image1abc': '<svg>Actual svg code</svg>' }

Also, I changed my Icon component from a dynamic component to a basic span tag with v-html:

<template>
<span v-html="getIcon(variant)"/>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import * as icons from '@/icons';

export default defineComponent({
props: {
variant: {
type: String,
required: true,
},
},
methods: {
getIcon: (variant: String)=> {
return icons.default[variant.toLowerCase() as keyof Object]
}
}
});

Cool, this resolves the first problem, and I was able to see all the images rendering in the application.

Problem 2: I also discovered that a few functionalities in my application were not rendering; while this was a very project-specific issue, I thought I’d share it in the hope that it can help someone else. In my project, we have a lot of permissions, and based on that, we render a few features. Now, we used to get these permissions from the BFF layer, and I was getting all of them from my BFF, but in a few places in my vue app, I was getting undefined for those permissions.

Solution: And again, after trying all possible things, I figured out that this issue was specific to a version conflict in the package-lock.json file. The version clash involved vue3, apollo client, and apollo-composable with vite. So instead of using different ranges for versions, I started hardcoding specific versions. I’m sharing the versions that helped me so that if anyone has any strange issues after vite migration, they can try the versions that worked for me:

"dependencies": {
"@apollo/client": "^3.7.1",
"@vitejs/plugin-vue": "3.2.0",
"@vue/apollo-composable": "4.0.0-alpha.19",
"graphql": "^16.5.0",
"vee-validate": "4.5.11",
"vite": "^3.0.0",
"vue": "3.2.33"
}

Tada!! I was overjoyed when the vite migration was completed and my application was up and running with all of its features and images. I’d also like to share the metrics that I collected following vitelline migration:

But as far as I know 😕

https://media.giphy.com/media/kBqbCNNeLWI0Y73NGt/giphy.gif

With all of this excitement, I began running my tests and discovered that they were all failing because we had removed the babel-related files, and Jest requires babel to run the tests:

https://media.giphy.com/media/dBsUACbhvDROt9pbFO/giphy.gif

Then I realized that there are ways to keep Jest with vite but with other dependencies like esbuild, etc., and that’s when I decided that if I had to put effort into something, I should put effort into Jest to Vitest migration rather than keeping Jest 🙌.

Thats all I want to share for vite migration, and I hope it will help a lot of people who have not yet moved to vite just because of some blockers. I personally believe the vite community is doing really well, and we have seen the metrics showing how fast it spins and builds up application. If you liked this vite migration journey of mine and are also interested in knowing my Jest ▶️ vitest migration story, you can find it here. In case you are in the process of vite migration and have some questions or need any help, feel free to drop a comment, I am happy to help 😊. And feedback is always welcome.

--

--