If you've been developing with Vue prior to 2021 there's a good chance that your build tool of choice was the Vue CLI. It's been the de-facto standard for scaffolding Vue.js projects for a while. Now though, Evan You's next generation build tool Vite, has been garnering a lot of attention and is a great alternative to the Vue CLI.
The primary reason for making the move is all about speed. Vite's development server is fast. Because it uses native browser support for JavaScript modules, the server start time is instant. The approach also means that Hot Module Replacement remains fast no matter the size of the application since the entire bundle doesn't have to be reconstructed.
To demonstrate, here are the benchmarks of a brand new Vue CLI project compared to the exact same project using Vite.
And remember this is just the scaffolded boilerplate. As your project grows, the Vue CLI version would just get incrementally slower, while Vite promises to perform at the same levels no matter the size of your project.
So you're convinced that Vite is right for you, how do you go about migrating your project from using Vue CLI to Vite?
In order to answer that question, I built a brand new project with Vue CLI and I'm going to go through the steps with you to convert it to Vite. Of course, you wouldn't typically be starting with a brand new Vue CLI project, but many of these steps will be the same for your established projects.
Also, I've chosen to work from a Vue 2 codebase, as most of you probably have your well established codebases still running on Vue 2. However, I've also made notes in the process below where things would differ for Vue 3.
Finally, if you don't want to walk through the whole process together with me, you can see a diff of the changes in this example repo.
The first step to migrating to Vite is to update the dependencies in package.json. We'll need to remove the Vue CLI related dependencies.
// package.json
"@vue/cli-plugin-babel": "~4.5.0", // remove
"@vue/cli-plugin-eslint": "~4.5.0", // remove
"@vue/cli-plugin-router": "~4.5.0", // remove
"@vue/cli-plugin-vuex": "~4.5.0", // remove
"@vue/cli-service": "~4.5.0", // remove
We can also remove the sass-loader as Vite provides built in support for the most common pre-processors out of the box. This will allow us to continue to use our CSS pre-processor of choice. Do note though that Vite recommends to use native CSS variables with PostCSS plugins that implement CSSWG drafts and author plain, future-standards-compliant CSS.
// package.json
"sass-loader": "^8.0.2" // remove
Lastly, we'll add Vite as a dependency, as well as the Vue plugin component for Vite in order to support Single File Components.
// package.json
"@vitejs/plugin-vue": "^1.6.1",
"vite": "^2.5.4",
Also, since we're migrating a Vue 2 project we also need to include the community maintained Vite plugin for Vue 2, in addition to the official Vue plugin. If we were working with Vue 3, this wouldn't be necessary.
// package.json
"vite-plugin-vue2" : "1.9.0" // add for Vue 2
With the Vite plugins installed, we can now also remove the vue template compiler as that's handled by the Vite Vue plugins.
// package.json
"vue-template-compiler": "^2.6.11" //remove (SFC support provided by vite vue plugin)
Since Vite is a next generation build tool, let's proceed optimistically by only supporting the most modern browsers. This will keep our builds as lean and as fast as possible.
Practically speaking, this means that we can remove Babel completely from our dependencies as most mobile and desktop evergreen browsers have near full support for all ES6 features. If you still need to support older browsers like Internet Explorer 11, Vite does provide an official plugin for that.
So, to remove Babel, first we'll delete the babel.config.js
file.
Next, since we already removed the @vue/cli-plugin-babel
dependency that requires babel itself, we just need to remove a couple other babel related dependencies from package.json.
// package.json
"babel-eslint": "^10.1.0", // remove
"core-js": "^3.6.5", // remove
With babel-eslint
now removed we need to remove it as a parser from our .eslintrc
file.
// .eslintrc
// remove
parserOptions: {
parser: "babel-eslint",
},
Note: If you don't have linting/formatting setup in your project you can skip to the next step but I highly encourage you to add it in if you don't have it already. Here's a great tutorial for getting it setup on your Vite powered Vue projects.
Lastly, while we're in .eslintrc
we need to add the environment es2022 as the src of the project now lives completely in ES Modules land. You can leave the node env in place as well, as some configuration files still run in the node environment.
// .eslintrc
env: {
node: true,
es2022: true, // 👈 add this
}
This change will also force us to update eslint
itself, as well as the eslint-plugin-vue
to support the es2021 environment.
$ npm install eslint@8 eslint-plugin-vue@8
In this step, let's configure Vite for our Vue.js project. Vite is configured via a vite.config.js
file in the root of the project. This is what the default vite.config.js
file looks like when generating a brand new Vite project for Vue using npm init vite@latest
.
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()]
})
We'll want to add 2 more things to it.
First, we'll import the Vue plugin from vite-plugin-vue2
instead of the official Vite Vue plugin.
// vite.config.js
import vue from '@vitejs/plugin-vue' // remove
import { createVuePlugin as vue } from "vite-plugin-vue2";
//...
If you're using Vue 3, of course, you don't have to do this.
Secondly, in order for the @
import alias to work as it did with Vue CLI, we'll need to add this bit.
// vite.config.js
//...
const path = require("path");
export default defineConfig({
//...
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});
Contrary to the Vue CLI, Vite actually puts the index.html file that holds the Vue.js application in the root of the project instead of the public directory, so you'll need to move it.
Also inside of index.html you'll want to make a few changes.
First we'll change instances of the <%= htmlWebpackPlugin.options.title %>
placeholder to hardcoded values.
// index.html
<!--remove-->
<title><%= htmlWebpackPlugin.options.title %></title>
<!--add-->
<title>Hard Coded Title</title>
//...
<!--remove-->
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
<!--add-->
<strong>We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
We'll also need to replace the <%= BASE_URL %>
placeholder, with an absolute path.
// index.html
<!--remove-->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!--add-->
<link rel="icon" href="/favicon.ico">
Lastly and most importantly, the JavaScript application is no longer auto injected so we need to include it like this:
<script type="module" src="/src/main.js"></script>
Back in package.json we'll also need to update the scripts. We'll change the old vue-cli-service
commands to Vite specific commands.
// package.json
"serve": "vue-cli-service serve", // remove
"build": "vue-cli-service build", // remove
"dev": "vite",
"build": "vite build",
"serve": "vite preview",
Note that the command to spin up the development server is no longer serve
. Vite uses dev
instead and serve
is used to preview the production build locally.
Also if you have linting enabled you should update the lint script to run eslint directly.
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src"
There is a lot of cross over between the way environment variables work in Vite and how they work in Vue CLI. For instance, your .env naming convention can remain the same.
.env # loaded in all cases
.env.local # loaded in all cases, ignored by git
.env.[mode] # only loaded in specified mode
.env.[mode].local # only loaded in specified mode, ignored by git
However, you can no longer access environment variables on a process
variable. Instead they can be found on import.meta.env
.
// router/index.js
base: process.env.BASE_URL, //remove
base: import.meta.env.BASE_URL,
Also, the VUE_APP_
prefix used to make declaring client exposed env variables more apparent is changed to VITE_
, so if you have any such environment variables you'll have to update them accordingly.
While our newly created Vue CLI project already does this, my bet is that your existing applications probably do not. So, you must ensure all imports of single file components end with the .vue
extension.
// Home.vue
import HelloWorld from "@/components/HelloWorld.vue"; // .vue is required
If this process is too overwhelming due to the size of your codebase you can configure vite so that this isn't required. This is accomplished by adding .vue
to the resolve.extensions
config option in vite.config.js
. Make sure you also manually include all the default extensions as well though as this option overrides the default.
// vite.config.js
//...
export default defineConfig({
plugins: [vue()],
resolve: {
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
//...
},
});
While this works, it should be avoided if at all possible. Why? Because according to the Vite docs: "Note it is NOT recommended to omit extensions for custom import types (e.g. .vue
) since it can interfere with IDE and type support."
Lastly, you can remove all the magic comments for naming your dynamic imports as these are webpack specific comments and don't mean anything to Vite.
// router/index.js
import(
/* webpackChunkName: "about" */ // remove
"../views/About.vue"
),
Instead Vite will automatically name your chunk based off of the original .vue file name combined with a cache busting hash, like this: About.37a9fa9f.js
After completing steps 1-8 above, your application should be ready to start running with Vite! Go ahead and start up your dev server with npm run dev
and see how fast Vite is for yourself.
If you have any other errors pop up at this point, please comment below and share them with the community, as well as any solutions you might have for them!
Finally, remember that you can see all these changes as a diff in this example repo to help with your migration.
Our goal is to be the number one source of Vue.js knowledge for all skill levels. We offer the knowledge of our industry leaders through awesome video courses for a ridiculously low price.
More than 200.000 users have already joined us. You are welcome too!
© All rights reserved. Made with ❤️ by BitterBrains, Inc.