やかんです。
「お気づきだろうか」と生意気な書き出しをしてしまいすみません。
※今回の記事はやや専門的な内容になります。
ブログの動作をちょっと速くした。
言葉の通りでございますが、今までブログの動き遅かったですよね。すみません。
具体的には、
- キャッシュを利用するようにした
- 取得するデータを小分けにした
という2点で、速度の改善を図りました。
実際どのくらい違いが出ているのかは分かりません
実装
今回リファクタしたのは投稿の取得周りです。一言で言うと、「今までは1つの投稿につき『前後の投稿』も同時に取得していたが、リファクタによって、『前後の投稿』を後回しにするようにした」となります。
「前後の投稿」とは、
↑これのことです。1つの投稿につき、次の投稿の情報、前の投稿の情報を取得したいというモチベでした。で、今までは「表示している投稿、その次の投稿、その前の投稿」を1度に取得していたから処理に時間がかかっていたんですが、それを「まず表示している投稿を取得して、それが終わったらその次の投稿、その前の投稿を取得する」というように処理を分けることで1つあたりの処理を軽くしました。
具体的な実装を以下に貼ります。wordpressとnextjsを利用したブログ作成などの参考になれば。
まずこれが投稿自体のコード。
import { format } from "date-fns";
import { Suspense } from "react";
import { MdCalendarMonth } from "react-icons/md";
import { AroundPostNav } from "@/components/around-post-nav";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { BeatLoader } from "@/components/ui/loader";
import { Separator } from "@/components/ui/separator";
import { WidthWrapper } from "@/components/width-wrapper";
import { getPost } from "@/lib/get-post";
type Props = {
slug: string;
};
export async function PostContent({ slug }: Props) {
const post = await getPost({ slug });
return (
<WidthWrapper>
<div className="flex flex-col space-y-8">
<h1 className="my-4 text-3xl">{post.title}</h1>
<div className="flex items-center justify-between">
<div className="flex items-center">
<div>
<Avatar>
<AvatarImage
src="/yakan.png"
width={400}
height={400}
alt="yakan"
/>
<AvatarFallback>やかん</AvatarFallback>
</Avatar>
</div>
<p>やかん</p>
</div>
<div className="flex items-center">
<MdCalendarMonth className="inline-block h-4 w-4" />
<p>{format(new Date(post.date), "yyyy/MM/dd")}</p>
</div>
</div>
<Separator className="my-4 bg-slate-400" />
<div
className="post-content"
dangerouslySetInnerHTML={{ __html: post.content }}
/>
<Separator className="my-4 bg-slate-400" />
<Suspense
fallback={
<div className="mt-[20vh] flex min-h-screen justify-center">
<BeatLoader color="#475569" />
</div>
}
>
<AroundPostNav post={post} />
</Suspense>
</div>
</WidthWrapper>
);
}
で、AroudPostNavというのが、「次の投稿」「前の投稿」のボタンの部分。コードは以下。
import { BsCaretLeftFill, BsCaretRightFill } from "react-icons/bs";
import { ButtonLink } from "@/components/ui/button-link";
import { getAroundPost } from "@/lib/get-post";
import { Post } from "@/lib/type";
type Props = {
post: Post;
};
export async function AroundPostNav({ post }: Props) {
const nextPost = await getAroundPost({ post, direction: "next" });
const prevPost = await getAroundPost({ post, direction: "prev" });
return (
<div className="flex justify-between">
{nextPost ? (
<ButtonLink href={`/${nextPost.slug}`}>
<BsCaretLeftFill className="inline-block h-4 w-4" />
次の投稿
</ButtonLink>
) : (
<div />
)}
{prevPost ? (
<ButtonLink href={`/${prevPost.slug}`}>
前の投稿
<BsCaretRightFill className="inline-block h-4 w-4" />
</ButtonLink>
) : (
<div />
)}
</div>
);
}
で、ちょいちょい登場しているgetPost関数、getAroundPost関数は以下。コメントとか雑ですが。
import { notFound } from "next/navigation";
import { z } from "zod";
import { getPostsPrimitive } from "@/lib/get-posts";
import { postSchema } from "@/lib/schemas";
import type { Post } from "@/lib/type";
import { wpPrefix } from "@/lib/utils";
/**
* 記事によってfetchリクエストの詳細を変える必要がある
*/
const pageSlugs = ["about-yakan", "about-blog", "privacy-policy"];
const fetchPost = ({ slug }: { slug: string }) => {
if (pageSlugs.includes(slug)) {
return fetch(`${wpPrefix()}/pages?slug=${slug}`);
} else {
return fetch(`${wpPrefix()}/posts?slug=${slug}`, {
// next: { revalidate: 3600 },
// NOTE: 現状、投稿した記事を編集することってあまりないから、キャッシュを使うようにする。
});
}
};
const getPostArgumentSchema = z.object({
slug: z.string(),
});
type GetPostArgument = z.infer<typeof getPostArgumentSchema>;
async function getPostPrimitive({ slug }: GetPostArgument) {
const res = await fetchPost({ slug })
.then((res) => res.json())
.catch((err) => {
console.error(err);
throw new Error("Failed to fetch data");
});
if (res.length === 0) {
notFound();
}
const post: Post = {
id: res[0].id,
date: res[0].date,
title: res[0].title.rendered,
slug: res[0].slug,
content: res[0].content.rendered,
};
return post;
}
/**
* 前後の記事の取得
*/
const getAroundPostArgumentSchema = z.object({
post: postSchema,
direction: z.union([z.literal("prev"), z.literal("next")]),
});
type GetAroundPostArgument = z.infer<typeof getAroundPostArgumentSchema>;
export async function getAroundPost({
post,
direction,
}: GetAroundPostArgument) {
const params =
direction === "prev"
? `before=${post.date}&order=desc`
: `after=${post.date}&order=asc`;
const aroundPost = await getPostsPrimitive({
params,
});
if (aroundPost.length === 0) {
return null;
}
return aroundPost[0];
}
/**
* getPost
*/
export async function getPost({ slug }: GetPostArgument) {
const post = await getPostPrimitive({ slug });
return post;
}
wordpressから記事を取得する場合の参考になれば嬉しいですが、なかなか曲者ですよねー。zod使ってサーバーサイドでバリデーションをかけたいところですが、wordpress rest apiの返り値がいつまでも同じスキーマを使ってくれるとも限りませんし、そもそも外部依存のスキーマを自分で実装したくはないです。壊れやすくなるので。
あ、あとキャッシュもためるようにしました。
ということで、こちらの記事は以上となります。最後までお読みいただき、ありがとうございます。