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 ?? []; + } +});