Vue 3 + vite работает в разработке, но не в производстве

Я разрабатываю веб-сайт, используя vue.js 3 и vite. Когда я работаю в режиме разработки, он работает нормально. Затем я создал веб-сайт, используя сборку пряжи, и запустил его с предварительным просмотром пряжи, но приложение показывает пустую страницу без каких-либо ошибок.

Вот мой код:

пакет.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.info(`😎  ${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.info(error);
}

роутер.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>

Я тоже использую макеты, вот мой 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>

А это превью в режиме разработки:

но в производственном режиме:

Я был бы признателен за некоторую помощь. Большое спасибо

У вас нет ошибок в консоли? Можете ли вы объяснить, почему вы установили esmExternals в разделе сборки vite.config (думаю, именно здесь ваша сборка ломается)?

Stefano Nepa 28.09.2022 16:02

@StefanoNepa консоль чистая, ничего не появляется, и я думаю, что esmExternals исходит из генератора проектов по умолчанию (create-vue , как упоминалось здесь https://vuejs.org/guide/quick-start.html#creating- a-vue-application:~:text=%3E%2‌​0npm%20init%20vue%40‌​последняя)

Nta 28.09.2022 16:40

похоже работают только файлы макетов (из скриншотов выше). Я пробовал это #72005194, но только пропал вид макетов

Nta 28.09.2022 16:51

Итак, ошибок нет, но как выглядит исходник страницы? то есть HTML, который браузер получает

Jaromanda X 29.09.2022 02:27

Я столкнулся с той же проблемой. Любое решение еще?

Muhammad Siddiqui 22.11.2022 12:47

@MuhammadSiddiqui я просто публикую ответ

Nta 23.11.2022 10:59
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
6
631
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Извините, я забыл ответить на вопрос. Просто добавьте async/await в функцию импорта, как показано ниже:

{
      path: "/login",
      name: "login",
      // add async/await 
      component: async () => await import("@/views/Test.vue"),
      // meta: {
      //   layout: "Auth",
      // },
},

Это относится к https://v3-migration.vuejs.org/breaking-changes/async-components.html#_3-x-syntax

Другие вопросы по теме