From 716b5535ae06a9ac1bb232cbd1530d070984f10a Mon Sep 17 00:00:00 2001
From: Julian Krauser <jkrauser209@gmail.com>
Date: Sat, 10 May 2025 17:37:58 +0200
Subject: [PATCH] change: loading performance

---
 app.vue                                       | 14 ++---
 components/Footer.vue                         |  5 +-
 components/Header.vue                         |  8 ++-
 components/base/ListImageItem.vue             | 10 ++--
 components/shared/EmphasiseArticle.vue        |  2 +-
 components/shared/List.vue                    |  6 +--
 composables/calculateTitle.ts                 | 11 ----
 composables/provideGlobal.ts                  |  9 ----
 composables/useGlobal.ts                      | 26 ++++++++++
 .../{calculateSitemap.ts => useSitemap.ts}    | 52 ++++++++-----------
 layouts/landing.vue                           |  3 +-
 pages/[...slug].vue                           | 41 ++++++++-------
 plugins/global.ts                             | 15 ++++++
 plugins/sitemap.ts                            | 25 +++++++++
 14 files changed, 129 insertions(+), 98 deletions(-)
 delete mode 100644 composables/calculateTitle.ts
 delete mode 100644 composables/provideGlobal.ts
 create mode 100644 composables/useGlobal.ts
 rename composables/{calculateSitemap.ts => useSitemap.ts} (54%)
 create mode 100644 plugins/global.ts
 create mode 100644 plugins/sitemap.ts

diff --git a/app.vue b/app.vue
index f4b9fe0..c8080a2 100644
--- a/app.vue
+++ b/app.vue
@@ -3,19 +3,13 @@
 </template>
 
 <script setup lang="ts">
-import calculateTitle from "./composables/calculateTitle";
-import provideGlobal from "./composables/provideGlobal";
-
-const { SEO } = await provideGlobal();
-const title = await calculateTitle();
-
-const { metaDescription, keywords } = SEO ?? {};
+const { seo, title } = useGlobal();
 
 useHead({
-  title: title,
+  title: title.value,
   meta: [
-    { name: "description", content: metaDescription },
-    { name: "keywords", content: keywords },
+    { name: "description", content: seo.value?.metaDescription },
+    { name: "keywords", content: seo.value?.keywords ?? "" },
   ],
 });
 </script>
diff --git a/components/Footer.vue b/components/Footer.vue
index b92195f..967da90 100644
--- a/components/Footer.vue
+++ b/components/Footer.vue
@@ -29,7 +29,6 @@
 </template>
 
 <script setup lang="ts">
-const title = await calculateTitle();
-const { footer } = await provideGlobal();
-const { links, maintained_by, copyright } = footer ?? {};
+const { footer, title } = useGlobal();
+const { links, maintained_by, copyright } = footer.value ?? {};
 </script>
diff --git a/components/Header.vue b/components/Header.vue
index 5973b3e..22cf62c 100644
--- a/components/Header.vue
+++ b/components/Header.vue
@@ -64,8 +64,6 @@
 </template>
 
 <script setup lang="ts">
-import provideGlobal from "../composables/provideGlobal";
-
 const {
   params: { slug: params },
 } = useRoute();
@@ -73,12 +71,12 @@ const {
 const runtimeConfig = useRuntimeConfig();
 const baseUrl = runtimeConfig.public.strapi.url;
 
-const { logo, navbar } = await provideGlobal();
+const { logo, navbar } = useGlobal();
 
 const open = ref(false);
 
 const navbar_sub_items = computed(() => {
-  if (!navbar) return [];
-  return navbar.navbar_items.find((ni) => ni.URL == params?.[0])?.navbar_sub_items;
+  if (!navbar.value) return [];
+  return navbar.value.navbar_items.find((ni) => ni.URL == params?.[0])?.navbar_sub_items;
 });
 </script>
diff --git a/components/base/ListImageItem.vue b/components/base/ListImageItem.vue
index fc4ac3b..432d2e5 100644
--- a/components/base/ListImageItem.vue
+++ b/components/base/ListImageItem.vue
@@ -4,10 +4,10 @@
     :class="allowNavigation ? '' : 'pointer-events-none'"
     :to="`${urlOverwrite ?? $route.path}/${data?.slug}`"
   >
-    <div class="w-full h-56 relative">
+    <div class="w-full h-56 min-h-56 relative">
       <NuxtPicture
         loading="lazy"
-        class="w-full h-full object-cover object-center"
+        class="w-full h-full min-h-full object-cover object-center"
         :src="data?.image?.url || logo?.url ? baseUrl + (data?.image?.url ?? logo?.url) : '/favicon.png'"
         :imgAttrs="{ class: 'w-full h-full object-cover object-center' }"
       />
@@ -20,11 +20,11 @@
       </h1>
     </div>
 
-    <div class="w-full h-44 relative bg-white px-2 py-5 flex flex-col justify-start items-start gap-2 overflow-y-auto">
+    <div class="w-full grow relative bg-white p-2 pb-4 flex flex-col justify-start items-start gap-2 overflow-y-auto">
       <h1>
         {{ data?.title }}
       </h1>
-      <p v-if="itemDate && lookup?.show_date" class="w-full text-[#5c5c5c]">
+      <p class="w-full text-[#5c5c5c] line-clamp-2 overflow-hidden">
         {{ itemDate }}
       </p>
       <p class="w-full text-[#5c5c5c] line-clamp-2 overflow-hidden">
@@ -42,7 +42,7 @@ import type Lookup from "../../types/collection/lookup";
 const runtimeConfig = useRuntimeConfig();
 const baseUrl = runtimeConfig.public.strapi.url;
 
-const { logo } = await provideGlobal();
+const { logo } = useGlobal();
 
 const props = defineProps({
   data: Object as PropType<BaseCollection>,
diff --git a/components/shared/EmphasiseArticle.vue b/components/shared/EmphasiseArticle.vue
index ade964f..8d99d1e 100644
--- a/components/shared/EmphasiseArticle.vue
+++ b/components/shared/EmphasiseArticle.vue
@@ -25,7 +25,7 @@ import type SharedEmphasiseArticle from "../../types/component/shared/emphasiseA
 import type Lookup from "../../types/collection/lookup";
 
 const { find } = useStrapi();
-const { data: lookup } = await useAsyncData("lookup", () =>
+const { data: lookup } = await useAsyncData(() =>
   find<Lookup>("collection-lookups", {
     filters: {
       collection: "articles",
diff --git a/components/shared/List.vue b/components/shared/List.vue
index 40b2348..b04ce21 100644
--- a/components/shared/List.vue
+++ b/components/shared/List.vue
@@ -113,13 +113,13 @@ const pagination = ref<Meta>({ page: 0, pageSize: 0, pageCount: 0, total: 0 });
 const activeYear = ref<number>(0);
 
 if (props.data?.lookup.list_with_date != "none") {
-  const { data: year } = await useAsyncData<Array<number>>("distinct-years", () =>
+  const { data: year } = await useAsyncData<Array<number>>(() =>
     $fetch(`${baseUrl}/api/custom/${props.data?.lookup.collection}/distinct-years`)
   );
   years.value = year.value ?? [];
   activeYear.value = years.value[0] ?? 0;
 }
-const { data: collections } = await useAsyncData("collection", () =>
+const { data: collections } = await useAsyncData(() =>
   find<BaseCollection>(props.data?.lookup.collection ?? "", {
     ...(props.data?.lookup?.list_with_date != "none"
       ? {
@@ -235,7 +235,7 @@ async function changeTimedData(year: number) {
       withCount: true,
     },
   });
-  console.log(data);
+
   collection.value = data?.data;
   pagination.value = (data?.meta.pagination as unknown as {
     page: number;
diff --git a/composables/calculateTitle.ts b/composables/calculateTitle.ts
deleted file mode 100644
index 2b9ab75..0000000
--- a/composables/calculateTitle.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import provideGlobal from "./provideGlobal";
-
-export default async function () {
-  const runtimeConfig = useRuntimeConfig();
-  const appTitle = runtimeConfig.public.app.title;
-
-  const { SEO } = await provideGlobal();
-  const { metaTitle } = SEO ?? {};
-
-  return metaTitle ?? appTitle;
-}
diff --git a/composables/provideGlobal.ts b/composables/provideGlobal.ts
deleted file mode 100644
index 61beaf8..0000000
--- a/composables/provideGlobal.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import type Global from "../types/single/global";
-
-export default async function () {
-  const { findOne } = useStrapi();
-  const { data: global } = await useAsyncData("global", () => findOne<Global>("global"));
-  const { logo, navbar, footer, SEO } = global.value?.data ?? {};
-
-  return { logo, navbar, footer, SEO };
-}
diff --git a/composables/useGlobal.ts b/composables/useGlobal.ts
new file mode 100644
index 0000000..922c1f5
--- /dev/null
+++ b/composables/useGlobal.ts
@@ -0,0 +1,26 @@
+import type BaseFile from "../types/component/baseFile";
+import type Footer from "../types/component/global/footer";
+import type Navbar from "../types/component/global/navbar";
+import type SEO from "../types/component/global/seo";
+import type Global from "../types/single/global";
+
+export const useGlobal = () => {
+  const global = useState<Global | null>("global");
+  const runtimeConfig = useRuntimeConfig();
+  const appTitle = runtimeConfig.public.app.title;
+
+  const logo = computed<BaseFile | null>(() => global.value?.logo ?? null);
+  const navbar = computed<Navbar | null>(() => global.value?.navbar ?? null);
+  const footer = computed<Footer | null>(() => global.value?.footer ?? null);
+  const seo = computed<SEO | null>(() => global.value?.SEO ?? null);
+  const title = computed<string>(() => seo.value?.metaTitle ?? appTitle);
+
+  return {
+    logo,
+    global,
+    navbar,
+    footer,
+    seo,
+    title,
+  };
+};
diff --git a/composables/calculateSitemap.ts b/composables/useSitemap.ts
similarity index 54%
rename from composables/calculateSitemap.ts
rename to composables/useSitemap.ts
index dbd7f51..86484e3 100644
--- a/composables/calculateSitemap.ts
+++ b/composables/useSitemap.ts
@@ -1,30 +1,15 @@
-import type Article from "../types/collection/article";
 import type Page from "../types/collection/page";
 import type { ComponentTypes } from "../types/component/baseComponent";
-import type SharedList from "../types/component/shared/list";
-import type Global from "../types/single/global";
 
-export default async function () {
-  const { find } = useStrapi();
+export const useSitemap = () => {
+  const { navbar, footer } = useGlobal();
+  const pages = useState<Page[]>("sitemap_pages");
 
-  const nuxtApp = useNuxtApp();
-  const { navbar, footer } = await nuxtApp.runWithContext(() => provideGlobal());
-  const { data: page_res } = await nuxtApp.runWithContext(() =>
-    useAsyncData("sitemap_pages", () =>
-      find<Page>("pages", {
-        filters: {
-          ref_only_access: false,
-        },
-      })
-    )
-  );
-  const pages = page_res.value?.data ?? [];
+  const sitemap = ref<sitemap>([]);
 
-  const accessableURLs: Array<{ path: string; origin: string; document?: string; hasCollection?: boolean }> = [];
-
-  for (const element of navbar?.navbar_items ?? []) {
+  for (const element of navbar.value?.navbar_items ?? []) {
     if (!element.default_active_child) {
-      accessableURLs.push({
+      sitemap.value.push({
         path: element.URL.startsWith("/") ? element.URL : `/${element.URL}`,
         origin: "navbar",
         document: element?.page?.documentId,
@@ -33,7 +18,7 @@ export default async function () {
     }
     for (const subelement of element.navbar_sub_items) {
       let url = `${element.URL}/${subelement.URL}`;
-      accessableURLs.push({
+      sitemap.value.push({
         path: url.startsWith("/") ? url : `/${url}`,
         origin: "navbar",
         document: subelement?.page?.documentId,
@@ -43,10 +28,10 @@ export default async function () {
     }
   }
 
-  for (const element of pages) {
+  for (const element of pages.value) {
     let url = element.slug.replaceAll("~", "/");
-    if (!accessableURLs.find((a) => a.path == url)) {
-      accessableURLs.push({
+    if (!sitemap.value.find((a) => a.path == url)) {
+      sitemap.value.push({
         path: url.startsWith("/") ? url : `/${url}`,
         origin: "page",
         document: element.documentId,
@@ -55,10 +40,10 @@ export default async function () {
     }
   }
 
-  for (const element of footer?.links ?? []) {
+  for (const element of footer.value?.links ?? []) {
     let url = element.URL.startsWith("/") ? element.URL : `/${element.URL}`;
-    if (!accessableURLs.find((a) => a.path == url) && !element.URL.startsWith("http")) {
-      accessableURLs.push({
+    if (!sitemap.value.find((a) => a.path == url) && !element.URL.startsWith("http")) {
+      sitemap.value.push({
         path: url,
         origin: "footer",
         document: undefined,
@@ -67,5 +52,12 @@ export default async function () {
     }
   }
 
-  return accessableURLs;
-}
+  return sitemap;
+};
+
+type sitemap = Array<{
+  path: string;
+  origin: string;
+  document?: string;
+  hasCollection?: boolean;
+}>;
diff --git a/layouts/landing.vue b/layouts/landing.vue
index 9b6bb80..c7de338 100644
--- a/layouts/landing.vue
+++ b/layouts/landing.vue
@@ -22,14 +22,13 @@
 </template>
 
 <script setup lang="ts">
-import provideGlobal from "../composables/provideGlobal";
 import type Homepage from "../types/single/homepage";
 
 const runtimeConfig = useRuntimeConfig();
 const baseUrl = runtimeConfig.public.strapi.url;
 const { findOne } = useStrapi();
 
-const { logo } = await provideGlobal();
+const { logo } = useGlobal();
 
 const { data: homepage } = await useAsyncData("homepage", () => findOne<Homepage>("homepage"));
 const { backdrop } = homepage.value?.data ?? {};
diff --git a/pages/[...slug].vue b/pages/[...slug].vue
index da75b80..943d7c7 100644
--- a/pages/[...slug].vue
+++ b/pages/[...slug].vue
@@ -12,14 +12,13 @@ import type Event from "../types/collection/event";
 import type Operation from "../types/collection/operation";
 import type Vehicle from "../types/collection/vehicle";
 import type Page from "../types/collection/page";
-import provideGlobal from "../composables/provideGlobal";
 
 const {
   params: { slug: params },
 } = useRoute();
 const { findOne, find } = useStrapi();
 
-const sitemap = await calculateSitemap();
+const sitemap = useSitemap();
 
 const detail = ref<Article | Operation | Event | Vehicle | undefined>(undefined);
 
@@ -27,10 +26,10 @@ const activePath = computed(() => {
   return "/" + (Array.isArray(params) ? params.join("/") : params);
 });
 const activePageBySitemap = computed(() => {
-  return sitemap.find((s) => s.path == activePath.value);
+  return sitemap.value.find((s) => s.path == activePath.value);
 });
 const similarestPage = computed(() => {
-  return sitemap.reduce(
+  return sitemap.value.reduce(
     (bestMatch, current) => {
       const currentMatchLength = current.path
         .split("/")
@@ -49,23 +48,27 @@ const similarestPage = computed(() => {
   );
 });
 
-const { data: pages } = await useAsyncData("pages", () =>
-  findOne<Page | Array<Page>>("pages", similarestPage.value?.document, {
-    populate: {
-      populate: "*",
-      content: {
+const { data: pages } = await useAsyncData(
+  () =>
+    findOne<Page | Array<Page>>("pages", similarestPage.value?.document, {
+      populate: {
         populate: "*",
+        content: {
+          populate: "*",
+        },
+        hero: {
+          populate: "*",
+        },
       },
-      hero: {
-        populate: "*",
+      filters: {
+        ...(!similarestPage.value?.document
+          ? { slug: Array.isArray(params) ? params.join("~") : params, ref_only_access: false }
+          : {}),
       },
-    },
-    filters: {
-      ...(!similarestPage.value?.document
-        ? { slug: Array.isArray(params) ? params.join("~") : params, ref_only_access: false }
-        : {}),
-    },
-  })
+    }),
+  {
+    default: () => null,
+  }
 );
 const page = computed(() => {
   return Array.isArray(pages.value?.data) ? pages.value.data[0] : pages.value?.data;
@@ -100,7 +103,7 @@ if (isCollectionDetail) {
   ];
 
   for (const element of collectionOfDetail) {
-    const { data: details } = await useAsyncData("detail", () =>
+    const { data: details } = await useAsyncData(() =>
       find<Article | Operation | Event | Vehicle>(element ?? "", {
         populate: "*",
         filters: {
diff --git a/plugins/global.ts b/plugins/global.ts
new file mode 100644
index 0000000..6ad638a
--- /dev/null
+++ b/plugins/global.ts
@@ -0,0 +1,15 @@
+import type Global from "../types/single/global";
+
+export default defineNuxtPlugin(async (nuxtApp) => {
+  const globalState = useState<Global | null>("global", () => null);
+
+  if (!globalState.value) {
+    const { findOne } = useStrapi();
+    const { data: global } = await useAsyncData("global", () => findOne<Global>("global"), {
+      server: true,
+      lazy: false,
+      default: () => {},
+    });
+    globalState.value = global.value?.data ?? null;
+  }
+});
diff --git a/plugins/sitemap.ts b/plugins/sitemap.ts
new file mode 100644
index 0000000..c2301f8
--- /dev/null
+++ b/plugins/sitemap.ts
@@ -0,0 +1,25 @@
+import type Page from "../types/collection/page";
+import type Global from "../types/single/global";
+
+export default defineNuxtPlugin(async (nuxtApp) => {
+  const pageState = useState<Page[]>("sitemap_pages", () => []);
+
+  if (!pageState.value) {
+    const { find } = useStrapi();
+    const { data: page_res } = await useAsyncData(
+      "sitemap_pages",
+      () =>
+        find<Page>("pages", {
+          filters: {
+            ref_only_access: false,
+          },
+        }),
+      {
+        server: true,
+        lazy: false,
+        default: () => {},
+      }
+    );
+    pageState.value = page_res.value?.data ?? [];
+  }
+});