Migrating a Vuejs Project From Webpack to Vite

The first fail To start out with, my strengths lie in the backend camp. I do frontend work as that is needed in any project of size, but I prefer backend development. So I’m sure there will be some face palm moments throughout this discussion, largely becasue it is in a realm that I’m not as comfortable with. Our existing project has a golang backend, and then we have three entry points into our frontend vuejs application (which is in vue 2.


Feb. 25, 2023 958 words

The first fail

To start out with, my strengths lie in the backend camp. I do frontend work as that is needed in any project of size, but I prefer backend development. So I’m sure there will be some face palm moments throughout this discussion, largely becasue it is in a realm that I’m not as comfortable with.

Our existing project has a golang backend, and then we have three entry points into our frontend vuejs application (which is in vue 2.x I’d also like to move to vue 3.x). First for the staff to interact with the system, second for the authenticated users to work with, and third a small one for unauthenticated users to get a bit of usage from.

The vite documentation starts out with a simple command of

npm create vite@latest

The command takes you through a set of steps to get your project off the ground. But one problem, it put all the vite files in a subdirectory of the same name. First face palm, I should have known that from the beginning. So now I need to get my vite dependencies and configs setup in my main project. So let’s look at a few things that npm create vite@latest created for us.

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Vue</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

Vite config

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

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
})

package.json

{
  "name": "luxweb",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "vue": "^3.2.45"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.0.0",
    "vite": "^4.1.0"
  }
}

Looking at the package.json that was created by our call to create a vite project, we can see that a few dependencies were pulled into the project:

  1. vue
  2. vitejs/plugin-vue
  3. vite

Getting started

So from our main project let’s pull in those dependencies. From our main project directory let’s add those dependencies.

  1. npm install -d vite
  2. npm install -d @vitejs/plugin-vue
    1. Here is my second problem, which I figured would occur, our older version of vue needs to be updated.
    2. npm install vue@latest
    3. Rerun the above command

Great, we’ve got our dependencies for building added and updated. Looks like we’re going to need to update our scripts section of our existing package.json to be able to use our vite builds. Because I already have some scripts existing for webpack, I’m going to add some more prefixed with a v if there are any duplicate names. To start out with I’m just going to add a build command:

"vbuild": "vite build"

So what do we do now?

npm run vbuild

And we fail, the message tells us that it could not resolve the entry module “index.html”. This is where things are going to get a bit more interesting, because we have an existing project, that is generated from a backend. Also we haven’t yet added a vite config yet, so let’s do that now.

I’ve added a vite.config.js file, with the vue plugin and the entry points for our build, that existed from a previous webpack build.

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

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  build: {
    outDir: "static",
    lib: {
      entry: {
        frontend: "./resources/js/frontend.js",
        app: "./resources/js/app.js",
        management: "./resources/js/management.js"
      }
    }
  }
})

After a lot of playing around with this config, I found out that the initial config is not what I needed. As that is for specific library projects. So after some more playing around with the config I changed a few things.

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

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  build: {
    emptyOutDir: false,
    outDir: "static/assets",
    rollupOptions: {
      input: {
        frontend: "./resources/js/frontend.js",
        app: "./resources/js/app.js",
        management: "./resources/js/management.js"
      },
      output: {
        chunkFileNames: 'js/[name].js',
        entryFileNames: 'js/[name].js',
        assetFileNames: ({name}) => {
          if(/\.css$/.test(name ?? '')) {
            return "css/[name][extname]";
          } else if (/\.(gif|jpe?g|png|svg)$/.test(name ?? '')) {
            return "images/[name][extname]";
          }

          return "[name][extname]";
        }
      }
    }
  }
})

Drum roll please

….

….

….

And it still doesn’t work! All I get now is a blank screen. Hold on, what is causing the problem now? Is it the vue2 to vue3 migration? Is it how vite is compiling the assets down? Spoiler alert, it is both of them!

Two main prolems

  1. Vue3 needs a runtime compiler if you have html inside of the mount div. Unlike vue2 which would happily chug along working with the template. So I needed to make sure that I had a blank div with an id to mount my application to. As well, I needed to move the markup that existed there before into its own vue component, that way I wouldn’t need any runtime compiling. I did this, and that is when I ran into problem number 2.

  2. This problem took a lot of guesses to get a handle on what was going on. This might just show some of my lack of frontent-compile-everything-even-though-it-is-just-a-scripting-language knowledge, but the way I was importing components from webpack was this:

const dashboard = import('./management/DashboardComponent.vue').default;

Turns out that doesn’t fly in vite. So I had to change my imports to this instead.

import dashboard from './management/DashboardComponent.vue';

To fix this, I finally had the sense to look at the code that was generated from the initial shell command that we ran at the beginning. That took me long enough!

One last thing

In my go template file, I had loaded the js like this.

<script src="/js/management.js"></script>

But, there was a not top level module error (something new to this usually desktop and backend developer). So I changed it to look like this.

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