I'm developing a website using vue.js 3 and vite. When i run on development mode, it works fine. Then i built the website using yarn build and run it with yarn preview, but the app shows blank page without any errors appear.
Here is my code :
package.json :
"name": "web-app-new",
"version": "0.0.0",
"scripts": {
"dev": "vite --port 5000 --host",
"build": "vite build",
"preview": "vite preview --port 4173",
"test:unit": "vitest --environment jsdom"
},
"dependencies": {
"axios": "^0.27.2",
"crypto-js": "^4.1.1",
"firebase": "^8.10.1",
"jwt-decode": "^3.1.2",
"maska": "^1.5.0",
"mdi-vue": "^3.0.13",
"moment": "^2.29.4",
"pinia": "^2.0.16",
"uuid": "^8.3.2",
"v-calendar": "^3.0.0-alpha.8",
"v-viewer": "^3.0.10",
"vue": "^3.2.39",
"vue-router": "^4.1.2",
"vue3-cookies": "^1.0.6",
"vuefire": "^2.2.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^3.0.1",
"@vitejs/plugin-vue-jsx": "^2.0.0",
"@vue/test-utils": "^2.0.2",
"autoprefixer": "^10.4.8",
"jsdom": "^20.0.0",
"postcss": "^8.4.16",
"tailwindcss": "^3.1.8",
"vite": "^3.0.3",
"vitest": "^0.18.1"
}
}
vite.config.js :
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), vueJsx()],
build: {
/** If you set esmExternals to true, this plugins assumes that
all external dependencies are ES modules */
commonjsOptions: {
esmExternals: true,
},
},
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
main.js :
import { createApp, markRaw } from "vue";
import { createPinia } from "pinia";
import mdiVue from "mdi-vue/v3";
import * as mdijs from "@mdi/js";
import App from "./App.vue";
import router from "./router";
import "./assets/main.css";
import "v-calendar/dist/style.css";
import utils from "./plugins/utils";
import external from "./plugins/utils.external";
// import { firestorePlugin } from "vuefire";
try {
const app = createApp(App);
const components = import.meta.globEager([
"./components/*.vue",
"./components/Atoms/*.vue",
"./components/Atoms/Image/*.vue",
"./components/Atoms/Button/*.vue",
"./components/Atoms/Input/*.vue",
"./components/Atoms/Tabs/*.vue",
"./components/Molecules/*.vue",
"./components/Molecules/Modal/*.vue",
"./components/Molecules/Transition/*.vue",
"./components/Organism/*.vue",
"./components/Organism/Absensi/*.vue",
"./components/Organism/Pengaturan/*.vue",
]);
Object.entries(components).forEach(([path, definition]) => {
// components/Atoms/Container.vue become => AtomsContainer
const componentName = path.replace(/(.vue|\/|\.|components|index)/g, "");
// Register component on this Vue instance
// console.log(` ${componentName} loaded`);
app.component(componentName, definition.default);
});
// @plugins
//@pinia
const pinia = createPinia();
pinia.use(({ store }) => {
store.$router = markRaw(router);
store.$app = app;
store.$globalProperties = app.config.globalProperties;
});
app.use(pinia);
//@others
// app.use(firestorePlugin, {});
app.use(mdiVue, {
icons: mdijs,
});
app.use(utils, {});
app.use(external, {});
app.use(router);
app.mount("#app");
} catch (error) {
console.log(error);
}
router.js :
import { createRouter, createWebHistory } from "vue-router";
import { useStorage } from "@/composables/storage";
import { useLoadingStore } from "@/stores/loading";
import { useUserStore } from "@/stores/user";
import { useClientStore } from "@/stores/client";
import { useWorkerStore } from "@/stores/worker";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
redirect: "/login",
},
{
path: "/login",
name: "login",
component: import("@/views/Test.vue"),
// meta: {
// layout: "Auth",
// },
},
{
path: "/daftar",
name: "daftar",
redirect: "/daftar",
component: () => import("@/components/Atoms/NestedWrapper.vue"),
children: [
{
path: "/daftar",
name: "daftar",
component: import("@/views/Daftar/index.vue"),
meta: {
icon: "history",
// layout: "Auth",
},
},
{
path: "/daftar/akun",
name: "daftar akun",
redirect: "/daftar/akun",
component: () => import("@/components/Atoms/NestedWrapper.vue"),
children: [
{
path: "/daftar/akun",
name: "daftar akun",
component: import("@/views/Daftar/Akun/index.vue"),
meta: {
icon: "account",
layout: "Plain",
validate: ({ next, userStore }) => {
((!userStore.$state?.form?.idNumber ||
!userStore.$state?.form?.email) &&
next("/daftar")) ||
next();
},
},
},
{
path: "/daftar/akun/buat",
name: "buat akun baru",
component: import("@/views/Daftar/Akun/Buat.vue"),
meta: {
icon: "account",
layout: "Plain",
validate: ({ next, userStore }) => {
((!userStore.$state?.form?.idNumber ||
!userStore.$state?.form?.email) &&
next("/daftar")) ||
next();
},
},
},
],
meta: {},
},
{
path: "/daftar/perusahaan",
name: "daftar perusahaan",
redirect: "/daftar/perusahaan",
component: () => import("@/components/Atoms/NestedWrapper.vue"),
children: [
{
path: "/daftar/perusahaan",
name: "daftar perusahaan",
component: import("@/views/Daftar/Perusahaan/index.vue"),
meta: {
icon: "history",
layout: "Plain",
},
},
{
path: "/daftar/perusahaan/akun",
name: "daftar akun perusahaan",
component: import("@/views/Daftar/Perusahaan/Akun.vue"),
meta: {
icon: "account",
layout: "Plain",
validate: ({ next, workerStore, clientStore }) => {
(!clientStore.$state?.form && next("/daftar")) || next();
},
},
},
],
meta: {},
},
{
path: "/daftar/pekerja",
name: "daftar pekerja",
redirect: "/daftar/pekerja",
component: () => import("@/components/Atoms/NestedWrapper.vue"),
children: [
{
path: "/daftar/pekerja",
name: "daftar pekerja",
component: import("@/views/Daftar/Pekerja/index.vue"),
meta: {
icon: "history",
layout: "Plain",
validate: ({ next, workerStore, clientStore }) => {
(!workerStore.$state?.form?.idNumber && next("/daftar")) ||
next();
},
},
},
{
path: "/daftar/pekerja/akun",
name: "daftar akun pekerja",
component: import("@/views/Daftar/Pekerja/Akun.vue"),
meta: {
icon: "account",
layout: "Plain",
validate: ({ next, workerStore, clientStore }) => {
((!workerStore.$state?.form ||
!workerStore.$state?.form?.idNumber) &&
next("/daftar")) ||
next();
},
},
},
],
meta: {},
},
],
meta: {
// layout: "Auth",
},
},
{
path: "/app",
redirect: "/app/beranda",
},
{
path: "/app/beranda",
name: "beranda",
component: import("@/views/Beranda.vue"),
meta: {
layout: "Auth",
},
},
{
path: "/app/404",
name: "404",
component: () => import("@/views/404.vue"),
meta: {
// type: [roles.All],
layout: "Auth",
hidden: true,
},
},
{
path: "/app/:pathMatch(.*)*",
redirect: "/app/wrong",
},
],
});
router.beforeEach(async (to, from, next) => {
const loadingStore = useLoadingStore();
const userStore = useUserStore();
const clientStore = useClientStore();
const workerStore = useWorkerStore();
loadingStore.setLoading({
skeleton: true,
});
if (to?.meta?.layout !== from?.meta?.layout) {
loadingStore.setLoading({
layout: true,
});
await new Promise((res) => setTimeout(() => res(true), 1000));
}
if (to?.path !== from?.path) {
loadingStore.setLoading({
global: true,
});
}
try {
if (useStorage("credentials")?.refresh) {
await userStore.statusUser();
}
if (userStore.isLoggedIn && !to.fullPath.includes("app")) {
return next({ path: "/app" });
}
if (!userStore.isLoggedIn && to.fullPath.includes("app")) {
return next({ path: "/login" });
}
if (to.meta?.validate && typeof to.meta?.validate === "function") {
return to.meta?.validate({
to,
from,
next,
userStore,
clientStore,
workerStore,
});
} else {
next();
}
} catch (error) {
return Promise.reject(error);
}
});
router.afterEach(async (to, from, failure) => {
const loadingStore = useLoadingStore();
const userStore = useUserStore();
try {
// if (!userStore.isLoggedIn) {
// router.push("/");
// }
} catch (error) {
return Promise.reject(error);
} finally {
loadingStore.clearLoading();
}
});
export default router;
App.vue :
import Layout from "@/layouts/index.vue";
import { onMounted } from "vue";
import { RouterLink, RouterView } from "vue-router";
import { useLoadingStore } from "./stores/loading";
onMounted(() => {
document.documentElement.style.scrollBehavior = "smooth";
document.documentElement.style.overflow = "auto";
useLoadingStore().setLoading({ layout: true });
});
</script>
<template>
<div class="min-h-screen">
<atoms-loading />
<atoms-alert />
<atoms-transition />
<!-- <atoms-swipe /> -->
<molecules-modal />
<!-- <router-view /> -->
<Layout />
</div>
</template>
Im also using layouts, here is my layouts/index.vue
:
import AppLayoutDefault from "./Default.vue";
import ErrorLayout from "./Error.vue";
import { markRaw, onErrorCaptured, onMounted, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
const layout = ref();
const route = useRoute();
watch(
() => route.meta?.layout || undefined,
async (metaLayout) => {
try {
const component =
metaLayout && (await import(/* @vite-ignore */ `./${metaLayout}.vue`));
layout.value = markRaw(component?.default || AppLayoutDefault);
} catch (e) {
layout.value = markRaw(ErrorLayout);
}
},
{ immediate: true }
);
onErrorCaptured(() => {
// layout.value = markRaw(ErrorLayout);
});
</script>
<template>
<component :is="layout"> <router-view /> </component>
</template>
And this is the preview on development mode :
but on production mode :
I would appriciate some help. Thanks a lot