Add Vue.js + Vite to an AdonisJS Application
AdonisJS is a Node.js backend framework. There's no frontend framework coming with it, everything is rendered on the server side.
This article will describe how to integrate Vue.js (and Vite) to an AdonisJS application.
You should first create a new project with a "web" structure and no Webpack Encore integration:
CUSTOMIZE PROJECT
❯ Select the project structure · web
❯ Enter the project name · hello-world
❯ Setup eslint? (y/N) · false
❯ Configure webpack encore for compiling frontend assets? (y/N) · false
Add Vue.js & Vite dependencies & Configure Vite #
# if using npm
npm install vue && npm install --dev vite @vitejs/plugin-vue
# if using yarn
yarn add vue && yarn add -D vite @vitejs/plugin-vue
# if using pnpm
pnpm install vue && pnpm install -D vite @vitejs/plugin-vue
Create Vite configuration at the root of your project (vite.config.ts
):
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';
// https://vitejs.dev/config/
export default defineConfig(({ command }) => {
return {
base: command === 'serve' ? '' : '/build/',
publicDir: 'fake_dir_so_nothing_gets_copied',
build: {
manifest: true,
outDir: 'public/build',
rollupOptions: {
input: 'resources/js/app.ts',
},
},
plugins: [vue()],
};
});
Reconfigure app startup scripts #
You'll need a parallel script runner like npm-run-all
:
# if using npm
npm install --dev npm-run-all
# if using yarn
yarn add -D npm-run-all
# if using pnpm
pnpm install -D npm-run-all
Then reconfigure your npm dev
script this way (package.json
excerpt):
{
"scripts": {
"dev": "run-p ace:serve vite:serve",
"ace:serve": "node ace serve --watch",
"vite:serve": "vite --host --port 3000"
}
}
At this point, the npm dev
command starts both AdonisJS & Vite. But we lack
the integration of both: how AdonisJS will instruct your browser that it should
load assets generated by Vite?
Integrate Vite into AdonisJS #
We'll create a ViteProvider
class that will generate the contents to be
included into AdonisJS pages:
Paste this into providers/ViteProvider.ts
:
import { ApplicationContract } from '@ioc:Adonis/Core/Application';
import Env from '@ioc:Adonis/Core/Env';
import { readFileSync } from 'fs';
export default class ViteProvider {
public static needsApplication = true;
constructor(protected app: ApplicationContract) {}
public async boot() {
const View = this.app.container.resolveBinding('Adonis/Core/View');
const served = () => {
const port = Env.get('VITE_PORT', 3000);
return `
<script type="module" src="http://localhost:${port}/@vite/client"></script>
<script type="module" src="http://localhost:${port}/resources/vue/app.ts" ></script>
`;
};
const built = () => {
const data = readFileSync('./public/build/manifest.json').toString();
const manifest = JSON.parse(data);
return `<script type="module" src="/build/${manifest['resources/vue/app.ts']['file']}"></script>`;
};
View.registerTag({
tagName: 'vite',
seekable: false,
block: false,
compile(_, buffer) {
buffer.outputRaw(
Env.get('NODE_ENV') === 'development' ? served() : built(),
);
},
});
}
}
Register the provider into .adonisrc.json
file (excerpt):
{
"providers": ["./providers/ViteProvider"]
}
Replace default view contents (resources/views/welcome.edge
):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/png" href="/favicon.ico" />
@vite ``
<title>AdonisJS + Vue.js + Vite</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
Create basic Vue.js application #
Create Vue.js application main entry point in resources/vue/app.ts
:
import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');
Create main component in resources/vue/App.vue
:
Welcome to AdonisJS + Vue.js + Vite
Adjust for production build #
We need to adjust build scripts as well (package.json
excerpt):
{
"scripts": {
"build": "run-s vite:build ace:build",
"ace:build": "node ace build --production",
"vite:build": "vite build"
}
}
Our application will be build through the npm run build
command.
This will fail because of the Vue.js code being processed by the TypeScript compiler.
To circumvent this, we add the Vue.js app folder to the TypeScript exclude paths
(tsconfig.json
excerpt):
{
"exclude": ["resources/vue/", "node_modules", "build"]
}
Final thoughts #
This article gives the steps for a basic Vue.js integration into AdonisJS.
The developer experience can be enhanced by integrating a few more libraries:
Let's discuss about this on Mastodon!