2
2

React + TypeScript: Next入門03 プリレンダリングとデータ取得

Last updated at Posted at 2023-01-26

Next.jsチュートリアルの公式作例を、TypeScriptも加えて、一からつくってみるシリーズの第3回です。アプリケーション内のディレクトリに収めたマークダウンファイルからデータを読み込んで、トップページに表示してみます。

プリレンダリングとは

プリレンダリングはNext.jsのデフォルトで、各ページはあらかじめレンダリングされます。ページのHTMLをクライアント側のJavaScriptにすべてつくらせるのでなく、Next.jsが静的な処理は先に済ませておくということです。プリレンダリングは、パフォーマンスとSEOを高めるのに役立つでしょう。

生成されたページそれぞれに関連づけられているのは、必要最小限のJavaScriptコードです。ブラウザがページを読み込むと、そのJavaScriptコードが実行され、完全にインタラクティブになります(「ハイドレーション」と呼ばれます)。

Next.jsには、つぎのふたつのプリレンダリングの仕方があります。違いは、いつページのHTMLをつくるかです(「Two Forms of Pre-rendering」参照)。

  • 静的サイト生成(Static Site Generation): ビルド時にHTMLを生成するプリレンダリング。そのあとは、あらかじめレンダリングされたHTMLが、リクエストのたびに再利用されます。
  • サーバーサイドレンダリング(Server-side Rendering): リクエストごとにHTMLを生成するプリレンダリングです。

可能であるかぎり、静的生成が推奨されます。ページのビルドが1回で済むため、サーバーサイドレンダリングより速く処理できるからです。このチュートリアルでは、静的生成を用います。

We recommend using Static Generation over Server-side Rendering for performance reasons. Statically generated pages can be cached by CDN with no extra configuration to boost performance. However, in some cases, Server-side Rendering might be the only option.
(「Pre-rendering」)

マークダウンファイルからデータを取り出す

ブログ投稿のデータはローカルのマークダウンファイルとし、アプリケーションの新たなディレクトリ(posts)に収めることにしましょう。つくるファイルはつぎのふたつです。GitHubの公開コードからデータをコピーしてください。

マークダウンファイルにはメタデータ(titledate)が含まれています。このメタデータを取り出すライブラリがgray-matterです。つぎのコマンドでインストールしてください。

npm install gray-matter

マークダウンファイルからデータを取り出す新たなユーティリティモジュールが以下のlib/posts.tsです(コード001)。なお、ディレクトリlibはトップレベルにつくってください。ライブラリgray-matterを用いた関数getSortedPostsDataが定められています。行う処理はつぎのふたつです。

  • マークダウンファイルからメタデータtitledate、およびファイル名を取り出します。
    • ファイル名を取得するのは投稿URLのidとして用いるためです。
  • 得られた投稿データは日付順に並べ替えてリストにします。

コード001■ユーティリティモジュール

lib/posts.ts
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';

const postsDirectory = path.join(process.cwd(), 'posts');
export type PostData = {
	id: string;
	date: string;
	title: string;
};
export const getSortedPostsData = (): PostData[] => {
	// /postsディレクトリ内のファイル名を得る
	const fileNames = fs.readdirSync(postsDirectory);
	const allPostsData = fileNames.map((fileName) => {
		// 拡張子".md"をファイル名から除く
		const id = fileName.replace(/\.md$/, '');
		// マークダウンファイルを文字列として読み込む
		const fullPath = path.join(postsDirectory, fileName);
		const fileContents = fs.readFileSync(fullPath, 'utf8');
		// gray-matterでpostのメタデータ部分を解析する
		const matterResult = matter(fileContents);
		// 取り出したデータにidを組み合わせる
		return {
			id,
			...matterResult.data as {date: string; title: string},
		};
	});
	// postを日付順に並べ替える
	return allPostsData.sort((a, b) => {
		if (a.date < b.date) {
			return 1;
		} else {
			return -1;
		}
	});
};

なお、このユーティリティモジュールlib/posts.tsのコードは、Next.jsの学習には大きく関わりません。コードのコメントを流し読むくらいで結構です。importしたライブラリについて知りたい方は、Noteをご参照ください。

そして、getSortedPostsDataimportするモジュールpages/index.tsxに加えるのが関数getStaticPropsです。この関数をexportすると、戻り値のプロパティがNext.jsによるページの静的生成に用いられます。そして、それらのプロパティは以下のコードのようにページのコンポーネント(Home)に引数として渡されるのです。

If you export a function called getStaticProps (Static Site Generation) from a page, Next.js will pre-render this page at build time using the props returned by getStaticProps.
(「getStaticProps」)

pages/index.tsx
import { getSortedPostsData } from '../lib/posts';
import type { PostData } from '../lib/posts';

type Props = {
	allPostsData: PostData[];
};
export const getStaticProps = async () => {
	const allPostsData = getSortedPostsData();
	return {
		props: {
			allPostsData,
		},
	};
};
// export default function Home() {
export default function Home({ allPostsData }: Props) {

	return (
		<Layout home>


			<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
				<h2 className={utilStyles.headingLg}>Blog</h2>
				<ul className={utilStyles.list}>
					{allPostsData.map(({ id, date, title }) => (
						<li className={utilStyles.listItem} key={id}>
							{title}
							<br />
							{id}
							<br />
							{date}
						</li>
					))}
				</ul>
			</section>
		</Layout>
	);
}

これで、マークダウンファイルから取り出されたメタデータが、投稿情報として日付順に表示されるようになりました。書き改めたトップページのモジュールpages/index.tsxの記述全体は、以下のコード002にまとめます(ソース01)。

図001■トップページに表示された投稿データ

qiita_2301003_001.png

コード002■トップページのモジュール

pages/index.tsx
import Head from 'next/head';
import Layout, { siteTitle } from '../components/layout';
import { getSortedPostsData } from '../lib/posts';
import type { PostData } from '../lib/posts';
import utilStyles from '../styles/utils.module.css';

type Props = {
	allPostsData: PostData[];
};
export const getStaticProps = async () => {
	const allPostsData = getSortedPostsData();
	return {
		props: {
			allPostsData,
		},
	};
};
export default function Home({ allPostsData }: Props) {
	return (
		<Layout home>
			<Head>
				<title>{siteTitle}</title>
			</Head>
			<section className={utilStyles.headingMd}>
				<p>[Your Self Introduction]</p>
				<p>
					(This is a sample website - you’ll be building a site like this on{' '}
					<a href="https://nextjs.org/learn">our Next.js tutorial</a>.)
				</p>
			</section>
			<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
				<h2 className={utilStyles.headingLg}>Blog</h2>
				<ul className={utilStyles.list}>
					{allPostsData.map(({ id, date, title }) => (
						<li className={utilStyles.listItem} key={id}>
							{title}
							<br />
							{id}
							<br />
							{date}
						</li>
					))}
				</ul>
			</section>
		</Layout>
	);
}

ソース01■Next入門03 プリレンダリングとデータ取得

いよいよ次回はプログ投稿のページを動的につくって、Next.jsチュートリアルの公式作例ができ上がりです。

React + TypeScript: Next入門シリーズ

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2