Migrating from vue-cli to vite

April 29, 2022

Why use vite

If you haven’t already heard of vite it is one of the new kids on the block for frontend tooling. It comes with a dev server and Javascript compiler/bundler and claims to rival the performance of tooling such as Webpack, Rollup and Parcel. It has really matured a lot in the last year or so and is growing in adoption, so much so that it has replaced vue-cli as the official tool for running and building Vue projects.

I recently went through the exercise of migrating a couple of our repos at Lob to using vite in place of vue-cli and the following documents the steps I took.

NOTE: these instructions are specific to Vue 3

Update vue

  1. Remove vue/compiler-sfc as it’s now already bundled in more recent versions of the vue core library

    npm uninstall @vue/compiler-sfc --save-dev
  2. Update vue to the latest

    npm install vue@latest

Remove unneeded vue-cli dependencies

  1. Remove unneeded vue-cli dependencies*

    npm uninstall @vue/cli-plugin-babel @vue/cli-plugin-eslint @vue/cli-plugin-router @vue/cli-service sass-loader vue-loader vue-template-compiler --save-dev

    *NOTE: keep sass-loader if using storybook

Remove unneeded babel/webpack dependencies/config

  1. Remove unneeded babel dependencies

    npm uninstall @babel/core babel-eslint babel-loader --save-dev npm uninstall core-js
  2. Remove babel.config.js

  3. (if exists) Remove parser: "babel-eslint" from parserOptions in .eslintrc (or .eslintrc.js)

Update eslint

  1. Update eslint and dependencies

    npm install eslint@8 eslint-plugin-vue@8 --save-dev
  2. Update eslintrc replacing env.node: true with:

    "env": { "es2021": true }

Install vite

  1. install vite library and dependencies

    npm install vite @vitejs/plugin-vue --save-dev
  2. add a vite.config.js

    import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; const path = require('path'); // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': path.resolve(__dirname, './src') }, extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] } });
  3. add jsconfig.json

    { "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"] }, "sourceMap": true } }

Update index.html

(Skip if your repo is a library and not a standalone application)

  1. Move index.html from /public to the root of the project
  2. Remove any <%= htmlWebpackPlugin.options.title %> tags and replace with strings
  3. Replace any <%= BASE_URL %> tags with absolute paths
  4. Replace <!-- built files will be auto injected --> with <script type="module" src="/src/main.js"></script>

Update scripts in package.json

  1. Update "serve": "vue-cli-service serve" to "dev": "vite"
  2. Update "build": "vue-cli-service build" to "build": "vite build"
  3. Update "lint": "vue-cli-service lint" to "lint": "eslint --ext .js,.vue ."
  4. Add "preview": "vite preview"

Update environment variables

  1. Everywhere that references process.env needs to be replaced with import.meta.env
  2. In environment variables replace all VUE_APP_ with VITE_
  3. Update special environment variables

Optional remaining clean up

  1. Add the .vue extension to all single file component imports (if not using the resolve.extensions property in vite.config.js)
  2. Remove any uses of /* webpackChunkName: ...*/

Update build

(Skip if your repo is a standalone application, this step is for library packages only)

  1. Configure “Library Mode”

  2. Add the following to your vite.config.js:

    1. Add to the build.rollupOptions object:

      resolve: { dedupe: ['vue'] }
    2. Add to the top level resolve object:

      dedupe: ['vue']

    (this is to prevent Vue from being bundled in the library package: https://github.com/vuejs/core/issues/4344)

Differences for a repo that uses Storybook

Because Storybook uses Webpack for running and building there are a couple of steps that need to differ for any repo that uses Storybook:

  1. Don’t remove (or add back if you already removed it) the sass-loader package
  2. Add vue-style-loader to development dependencies (it was packaged with vue/cli-service previously so now it needs to be an explicit dependency) npm install sass-loader vue-style-loader --save-dev

Examples

Here I’m including a couple examples of the vite.config.js I ended up with for a library and a standalone application:

Library

import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; const path = require('path'); // https://vitejs.dev/config/ export default defineConfig({ build: { lib: { entry: path.resolve(__dirname, 'src/main.js'), name: 'my-library', fileName: (format) => `my-library.${format}.js` }, rollupOptions: { // make sure to externalize deps that shouldn't be bundled // into your library external: ['vue'], output: { // Provide global variables to use in the UMD build // for externalized deps globals: { vue: 'Vue' } }, resolve: { dedupe: ['vue'] } } }, plugins: [vue()], resolve: { alias: { '@': path.resolve(__dirname, './src') }, dedupe: ['vue'], extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] } });

Application

import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; const path = require('path'); // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue() ], resolve: { alias: { '@': path.resolve(__dirname, './src') }, extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] }, server: { host: true, port: 8888, strictPort: true }, preview: { port: 8888, strictPort: true } });

Resources

The following are some helpful resources I used when performing these migrations: