7. プリレンダリング
変更前ソース
機能概要
Next.jsはプリレンダリングを行い、JavaScriptが無効な状態でも、ページのコンテンツを表示できます。一方、React.jsではすべての描写をJavaScriptで行うため、JavaScriptが無効の場合は何も表示がされません。
機能詳細
Next.jsのプリレンダリングはさらに2つの方式があります。
- 静的生成(Static-site Generation(SSG))
- サーバーサイドレンダリング(Server-side Rendering(SSR))
SSGの場合、ビルド時にすべてのHTMLを作成します。SSRの場合、WebサーバーにリクエストがあるたびにHTMLを作成します。
パフォーマンスの観点では、SSGのほうがCDNで提供できるため優れています。Next.jsではSSGとSSRをページごとに選択できますので、基本はSSGで作成し、必要に応じてSSRを使うというような使い分けをするとよいでしょう。
今回はSSGの実装を行います。
実装方法
ブログのデータをファイルで用意します。
「posts」フォルダを作成します(「pages/posts」ディレクトリとは別です)。
「pre-rendering.md」と「ssg-ssr.md」2つのファイルを作成します。
「pre-rendering.md」を編集します。
--- title: 'Two Forms of Pre-rendering' date: '2020-01-01' --- Next.js has two forms of pre-rendering: **Static Generation** and **Server-side Rendering**. The difference is in **when** it generates the HTML for a page. - **Static Generation** is the pre-rendering method that generates the HTML at **build time**. The pre-rendered HTML is then _reused_ on each request. - **Server-side Rendering** is the pre-rendering method that generates the HTML on **each request**. Importantly, Next.js lets you **choose** which pre-rendering form to use for each page. You can create a "hybrid" Next.js app by using Static Generation for most pages and using Server-side Rendering for others.
「ssg-ssr.md」を編集します。
--- title: 'When to Use Static Generation v.s. Server-side Rendering' date: '2020-01-02' --- We recommend using **Static Generation** (with and without data) whenever possible because your page can be built once and served by CDN, which makes it much faster than having a server render the page on every request. You can use Static Generation for many types of pages, including: - Marketing pages - Blog posts - E-commerce product listings - Help and documentation You should ask yourself: "Can I pre-render this page **ahead** of a user's request?" If the answer is yes, then you should choose Static Generation. On the other hand, Static Generation is **not** a good idea if you cannot pre-render a page ahead of a user's request. Maybe your page shows frequently updated data, and the page content changes on every request. In that case, you can use **Server-Side Rendering**. It will be slower, but the pre-rendered page will always be up-to-date. Or you can skip pre-rendering and use client-side JavaScript to populate data.
開発サーバーを止めて、先ほどのファイルのメタデータを読むために次のnpmをインストールします。
npm install gray-matter
「lib」フォルダを作成し、「posts.js」ファイルを作成します。
import fs from 'fs'; import path from 'path'; import matter from 'gray-matter'; const postsDirectory = path.join(process.cwd(), 'posts'); export function getSortedPostsData() { // posts ディレクトリのファイル一覧を取得 const fileNames = fs.readdirSync(postsDirectory); const allPostsData = fileNames.map((fileName) => { // ".md" 拡張子を消してidとして読み込む const id = fileName.replace(/\.md$/, ''); // ファイルを読み込む const fullPath = path.join(postsDirectory, fileName); const fileContents = fs.readFileSync(fullPath, 'utf8'); // gray-matter でメタデータを取得 const matterResult = matter(fileContents); // IDとメタデータを返す return { id, ...matterResult.data, }; }); // 投稿データを日付順でソートする return allPostsData.sort(({ date: a }, { date: b }) => { if (a < b) { return 1; } else if (a > b) { return -1; } else { return 0; } }); }
「pages/index.js」に次のコードを追加します。
import Head from 'next/head'; import Layout, { siteTitle } from '../components/layout'; import utilStyles from '../styles/utils.module.css'; import { getSortedPostsData } from '../lib/posts'; // 新たにインポート // パラメーターに allPostsData を追加 export default function Home({ allPostsData }) { return ( // Layout に home であることを明示的に指定 <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> ); } // 静的生成時にデータを取得する処理を追加 export async function getStaticProps() { const allPostsData = getSortedPostsData(); return { props: { allPostsData, }, }; }
静的生成(SSG)時は「getStaticProps」で可変するデータを読み取ってページに埋め込むことができます。この時注意していただきたいのはあくまでもビルド時点に呼び出されるということです。
「npm run dev」で動かしているときは利便性が重視され、ブラウザからのリクエスト毎にデータを読み込みます。開発時は動いたけど本番ではうまく動かないという時はこの点に注意してください。
また、SSRの場合は「getServerSideProps」を同様に実装することでリクエスト毎にデータを読み込み、ページを動的に書き換えることが可能です。本記事ではSSRは取り扱いませんので、詳細は公式ドキュメントのgetServerSidePropsをご確認ください。
動作確認
http://localhost:3000/をブラウザで開いて、次のように表示されます。