import Head from 'next/head'; import { GetStaticProps, GetStaticPropsContext } from 'next'; import Link from 'next/link'; // pages/ssg/index.tsxのコードと共通している部分が多いが、 // 解説が1ファイルで完結できるようにあえて // 別ファイルに用意しない形で記述している。 // // また、 // この中身のファイルは実質pages/sssg/index.tsxと同じで、 // ISG解説用のサンプルコードのメインはpages/isg/posts/[id].tsxとなる type Post = { userId: number; id: number; title: string; body: string; }; interface ISGProps { posts: Post[]; } export default function ISG({ posts }: ISGProps) { return ( div> Head> title>ISGの解説用ページ(Postリンク一覧)/title> link rel="icon" href="/favicon.ico" /> /Head> main> h1>Postのリンク一覧/h1> ul> {posts.map((post) => { return ( li key={post.id}> Link // https://nextjs.org/docs/api-reference/next/link // "next/link" はデフォルトでリンク先のデータの取得(prefetch)やISGを実行させてしまう // ISGの動作確認がわかりやすいように、ここではprefetchの機能をオフにしている prefetch={false} href={`/isg/posts/${post.id}`} > {post.title} /Link> /li> ); })} /ul> /main> /div> ); } export const getStaticProps: GetStaticProps = async ( _context: GetStaticPropsContext ) => { const res = await fetch('https://jsonplaceholder.typicode.com/posts'); const posts = (await res.json()) as Post[]; return { props: { posts, }, }; };
import Head from 'next/head'; import { GetStaticProps, GetStaticPropsContext, GetStaticPaths, GetStaticPathsContext, } from 'next'; // pages/ISG/posts/[id].tsxと共通するコードが複数あるが // 解説が1ファイルで完結できるようにあえて // 別ファイルに用意しない形で記述している type Post = { userId: number; id: number; title: string; body: string; }; interface ISGProps { post: Post; } export default function ISGPostsId({ post }: ISGProps) { return ( div> Head> title>ISGの解説用ページ(Post詳細)/title> link rel="icon" href="/favicon.ico" /> /Head> main> h1>Post詳細(ISG)/h1> div> p>Post ID: {post?.id}/p> p>User ID: {post?.userId}/p> p>Title: {post?.title}/p> p>Body: {post?.body}/p> /div> /main> /div> ); } type ISGParams = { id: string; }; export const getStaticPaths: GetStaticPaths = async ( _context: GetStaticPathsContext ) => { return { /** * "next build"でHTMLファイルを生成しないようにするため * 「id: 1」のPost情報のHTMLだけSSGを行うようにしている。 * (それ以外の「id:2 ~ id:100」はISGでHTMLを生成) * * 「id:1~id:100」の詳細は以下を参照 * https://jsonplaceholder.typicode.com/posts */ paths: [{ params: { id: '1' } }], /*** * falsebackの値は true もしくは 'blocking' にすると、 * ISGの機能が有効となる * * ---------------- * fallback: true * https://nextjs.org/docs/basic-features/data-fetching#fallback-true * ---------------- * * SSGで生成していないファイルに初回アクセスした場合に、 * ページコンポーネントの実装を元に最低限のSSRを行い * 必要に応じてCSRを行う。 * (getStaticPropsで記述したコードはCSRのような形でデータを取得する) * * 裏側ではアクセスしたURLに対応するHTMLも生成する * (アクセス毎に1ファイルのみのSSGを行う = ISG)。 * * 同じURLへの2回目以降のアクセスに関しては、 * ISGによりHTMLファイルを生成しているため、 * クライアントにはISGで生成済みのHTMLを返す * * * ---------------- * fallback: 'blocking' * https://nextjs.org/docs/basic-features/data-fetching#fallback-blocking * ---------------- * * 「fallback: true」では、 * getStaticPropsの処理の完了を待たずに、 * 最低限のHTMLをクライアントに返し、 * その後CSRでページに必要な情報の取得、レンダリングを行っていたが、 * * 「fallback: 'blocking'」では、 * getStaticPropsで記述したコードはgetServerSidePropsのような動作を行う。 * * つまり、サーバー側で一通りのHTMLの生成が完了するまでは * クライアントへのレスポンスは待機状態となる。 * * 初回アクセスでISGを行いHTMLの生成、 * 2回目以降のアクセスでは生成済みのHTMLを返すという動作は * 「fallback: true」と同じ * * デベロッパーツールでJavaScriptのdebugger設定で * disableにすると、ISGが実行がされるときの動作の違いが確認しやすい。 * →https://developers.google.com/web/tools/chrome-devtools/javascript/disable * * - fallback: true(CSR) * - → クライアント側でJSが実行できないため、CSRが行われず中途半端な状態のページが表示される(APIの実行もされない) * - fallback: 'blocking'(SSR) * - → クライアント側でJSが実行できないが、SSRの動きとなり完全な形でページが表示される */ fallback: true, // true or 'blocking' for ISG }; }; export const getStaticProps: GetStaticProps = async ( context: GetStaticPropsContext ) => { const params = context.params as ISGParams; const postId = params.id; const res = await fetch( `https://jsonplaceholder.typicode.com/posts/${postId}` ); const post = (await res.json()) as Post; return { props: { post, }, }; };