From 5325a08bc39a1c83a6507ec41b13e31b39ceb12e Mon Sep 17 00:00:00 2001 From: gbanyan Date: Sun, 15 Mar 2026 17:31:23 +0800 Subject: [PATCH] perf: memoize post queries and reduce JSON-LD bloat --- app/blog/[slug]/page.tsx | 2 -- app/layout.tsx | 2 ++ lib/posts.ts | 49 ++++++++++++++++++++++++++++++---------- next-env.d.ts | 2 +- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/app/blog/[slug]/page.tsx b/app/blog/[slug]/page.tsx index 793f300..6848b1c 100644 --- a/app/blog/[slug]/page.tsx +++ b/app/blog/[slug]/page.tsx @@ -145,8 +145,6 @@ export default async function BlogPostPage({ params }: Props) { wordCount: wordCount, readingTime: `${readingTime} min read`, }), - articleBody: textContent.slice(0, 5000), - inLanguage: siteConfig.defaultLocale, url: postUrl, }; diff --git a/app/layout.tsx b/app/layout.tsx index cb71124..107dc7f 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -130,6 +130,8 @@ export default async function RootLayout({ + + = new Map(); +let _neighborsCache: Map = new Map(); +let _tagsCache: { tag: string; slug: string; count: number }[] | null = null; + export function getAllPostsSorted(): Post[] { - return [...allPosts].sort((a, b) => { + if (_sortedCache) return _sortedCache; + _sortedCache = [...allPosts].sort((a, b) => { const aDate = a.published_at ? new Date(a.published_at).getTime() : 0; const bDate = b.published_at ? new Date(b.published_at).getTime() : 0; return bDate - aDate; }); + return _sortedCache; } export function getPostBySlug(slug: string): Post | undefined { @@ -38,24 +45,31 @@ export function getTagSlug(tag: string): string { } export function getAllTagsWithCount(): { tag: string; slug: string; count: number }[] { - const map = new Map(); + if (_tagsCache) return _tagsCache; + const map = new Map(); for (const post of allPosts) { if (!post.tags) continue; - for (const tag of post.tags) { - map.set(tag, (map.get(tag) ?? 0) + 1); + for (const postTag of post.tags) { + map.set(postTag, (map.get(postTag) ?? 0) + 1); } } - return Array.from(map.entries()) + _tagsCache = Array.from(map.entries()) .map(([tag, count]) => ({ tag, slug: getTagSlug(tag), count })) .sort((a, b) => { if (b.count === a.count) return a.tag.localeCompare(b.tag); return b.count - a.count; }); + return _tagsCache; } export function getRelatedPosts(target: Post, limit = 3): Post[] { + const cacheKey = `${target._id}-${limit}`; + if (_relatedCache.has(cacheKey)) { + return _relatedCache.get(cacheKey)!; + } + const targetTags = new Set(target.tags?.map((tag) => tag.toLowerCase()) ?? []); const candidates = getAllPostsSorted().filter((post) => post._id !== target._id); @@ -84,28 +98,39 @@ export function getRelatedPosts(target: Post, limit = 3): Post[] { .slice(0, limit) .map((entry) => entry.post); + let result: Post[]; if (scored.length >= limit) { - return scored; + result = scored; + } else { + const fallback = candidates.filter( + (post) => !scored.some((existing) => existing._id === post._id) + ); + result = [...scored, ...fallback.slice(0, limit - scored.length)].slice(0, limit); } - const fallback = candidates.filter( - (post) => !scored.some((existing) => existing._id === post._id) - ); - - return [...scored, ...fallback.slice(0, limit - scored.length)].slice(0, limit); + _relatedCache.set(cacheKey, result); + return result; } export function getPostNeighbors(target: Post): { newer?: Post; older?: Post; } { + const cacheKey = target._id; + if (_neighborsCache.has(cacheKey)) { + return _neighborsCache.get(cacheKey)!; + } + const sorted = getAllPostsSorted(); const index = sorted.findIndex((post) => post._id === target._id); if (index === -1) return {}; - return { + const result = { newer: index > 0 ? sorted[index - 1] : undefined, older: index < sorted.length - 1 ? sorted[index + 1] : undefined }; + + _neighborsCache.set(cacheKey, result); + return result; } diff --git a/next-env.d.ts b/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.