Next.jsチュートリアル目次と学習内容
章 | タイトル | 学習内容 |
---|---|---|
1 | Create a Next.js App | Next.jsアプリの作成方法 |
2 | Navigate Between Pages | Next.jsにおけるページ遷移方法など React.jsを拡張したNext.jsの<Link /> タグなどを使うことでレスポンス向上できるなど |
3 | Assets, Metadata, and Css | 画像やCSSの適用方法などについて |
4 | Pre-rendering and Data Fetching | Next.jsの特色であるPre-rendering 機能について |
5 | Dynamic Routes | 各ページのURLを動的に生成しする方法 |
6 | API Routes | APIのルートについて |
7 | Deploying Your Next.js App | Nex.jsアプリのデプロイ方法 |
その5 Dynamic Routes
5-1. Dynamic Routes
- 個々のブログページのURLはブログのデータに依存するようにしたいので、動的なルートを使用する
- 任意のページのURLがデータに依存する場合は、
dynamic routes
を導入する
- 任意のページのURLがデータに依存する場合は、
学べること
getStaticPaths
を使用してdynamic routes
でページを静的に生成する方法(5-3, 5-4)- ブログ投稿ごとにデータを取得するための
getStaticProps
の書き方(5-5) - remarkを使用してマークダウンをレンダリングする方法(5-6)
- 日付文字列をきれいに表示する方法(5-7)
- 動的ルートを使用してページにリンクする方法(5-8)
- 動的ルートに関するいくつかの有用な情報(5-9)
5-2. Steup
5-3. Page Path Depends on External Data
- 前回のレッスンでは、ページのコンテンツが外部データに依存するケースを取り上げました。
インデックスページをレンダリングするために必要なデータをフェッチするために、getStaticPropsを使用しました。
このレッスンでは、各ページのパスが外部データに依存している場合について説明します。
- Next.jsでは、外部データに依存するパスをもつページを静的に生成することができます。これによりNext.jsでは動的URLの利用が可能になります。
外部データに依存するページパス
- 外部データ(データベースなど)に依存するページをプリレンダリングできる
- 外部ページから
id
を引っ張り出して、/posts/[id]
のようなページを生成する
ダイナミックルートでページを静的に生成する方法
- ブログ投稿のための動的なルートを作成したい
ステップの概要
- 以下のステップを踏むことで実現できます。まだ変更する必要はありません。次のページですべて行います。
- まず、
pages/postsの下に[id].js
というページを作成する(Next.jsでは、[
で始まり]
で終わるページがダイナミックルートになる) pages/posts/[id].js
には、これまで作成した他のページと同じように、投稿ページをレンダリングするコードを記述します。
import Layout from '../../components/layout'; export default function Post() { return <Layout>...</Layout>; }
- さて、ここからが新機能です。
- このページから
getStaticPaths
という非同期関数をエクスポートします。 - この関数では、
id
に対して取り得る値のリストを返す必要があります。
import Layout from '../../components/layout'; export default function Post() { return <Layout>...</Layout>; } export async function getStaticPaths() { // Return a list of possible value for id }
- 最後に、
getStaticProps
を再び実装する必要があります - 今回は、与えられたidを持つブログ記事に必要なデータを取得します。
import Layout from '../../components/layout'; export default function Post() { return <Layout>...</Layout>; } export async function getStaticPaths() { // Return a list of possible value for id } export async function getStaticProps({ params }) { // Fetch necessary data for the blog post using params.id }
[まとめ]
/posts/<id>
というパスを持つページを静的に生成する(<id>
はファイル名から決定され、これを動的と呼んでいる?)- 具体的には、
/pages/posts/[id].js
というページを作り、下記の内容を含める([
と]
がNext.jsにおいて動的ページであることを示している)- このページを描画するためのReact要素
id
に基づく配列を返すgetStaticPaths
関数id
を使用して、ブログの投稿データを取得するgetStaticProps
関数
- 具体的には、
5-4. Implement getStaticPaths
getStaticPathsの実装
- まず、ファイルを設定しましょう。
pages/posts
ディレクトリの中に[id].js
というファイルを作成します。- また、pages/postsディレクトリ内のfirst-post.jsを削除してください。これはもう使いません。
- そして、pages/posts/[id].jsをエディタで開き、以下のコードを貼り付けます。後で...を記入します。
import Layout from '../../components/layout'; export default function Post() { return <Layout>...</Layout>; }
- 次に、lib/posts.jsを開き、以下のgetAllPostIds関数を一番下に追加します。
- これは、postsディレクトリにあるファイル名(.mdを除く)のリストを返します。
export function getAllPostIds() { const fileNames = fs.readdirSync(postsDirectory); // Returns an array that looks like this: // [ // { // params: { // id: 'ssg-ssr' // } // }, // { // params: { // id: 'pre-rendering' // } // } // ] return fileNames.map((fileName) => { return { params: { id: fileName.replace(/\.md$/, ''), }, }; }); }
重要: 返されるリストは単なる文字列の配列ではなく、上のコメントのようなオブジェクトの配列である必要があります。
- 各オブジェクトはparams
キーを持ち、id
キーを持つオブジェクトを含んでいなければなりません (ファイル名に[id]を使っているからです)。
- そうでないと、getStaticPaths
は失敗します。
- 最後に、getAllPostIds関数をインポートして、getStaticPathsの内部で使用することにします。
pages/posts/[id].js
を開き、エクスポートしたPostコンポーネントの上に以下のコードをコピーしてください。
import { getAllPostIds } from '../../lib/posts'; export async function getStaticPaths() { const paths = getAllPostIds(); return { paths, fallback: false, }; }
- paths は、getAllPostIds() が返す既知のパスの配列を含みます。
- この配列には pages/posts/[id].js で定義されたパラメータが含まれます。詳しくはpathsキーのドキュメントをご覧ください。
- fallback: false を無視する - 後で説明します。
- これでほぼ完成です。しかし、まだgetStaticPropsを実装する必要があります。
次のページでそれをやってみましょう。
5-5. Implement getStaticProps
getStaticPropsの実装 - 指定されたidの投稿をレンダリングするために、必要なデータを取得する必要があります。 - そのためには、lib/posts.jsをもう一度開き、以下のgetPostData関数を一番下に追加します。これは、idに基づいた投稿データを返します。
export function getPostData(id) { const fullPath = path.join(postsDirectory, `${id}.md`); const fileContents = fs.readFileSync(fullPath, 'utf8'); // Use gray-matter to parse the post metadata section const matterResult = matter(fileContents); // Combine the data with the id return { id, ...matterResult.data, }; }
- 次に、pages/posts/[id].jsを開き、この行を置き換えます。
import { getAllPostIds } from '../../lib/posts';
を以下のコードで実行します。
import { getAllPostIds, getPostData } from '../../lib/posts'; export async function getStaticProps({ params }) { const postData = getPostData(params.id); return { props: { postData, }, }; }
- 投稿ページでは、getStaticPropsのgetPostData関数を使って、投稿データを取得し、propsとして返すようになりました。
- では、Postコンポーネントを更新してpostDataを使えるようにしましょう。
- pages/posts/[id].jsで、エクスポートしたPostコンポーネントを以下のコードに置き換えてください。
export default function Post({ postData }) { return ( <Layout> {postData.title} <br /> {postData.id} <br /> {postData.date} </Layout> ); }
- 以上です。これらのページを見てみてください。
- http://localhost:3000/posts/ssg-ssr
- http://localhost:3000/posts/pre-rendering
- 各ページのブログデータを見ることができるはずです。
素晴らしい!ダイナミックルートの生成に成功しました。 何か問題がありますか? もしエラーに遭遇したら、ファイルに正しいコードがあることを確認してください。 pages/posts/[id].js はこのようになっているはずです。 lib/posts.jsはこのようになっているはずです。 (それでも動かない場合) 残りのコードは以下のようになっているはずです。 それでもうまくいかない場合は、GitHub Discussionsでコミュニティに質問してください。GitHub にコードをプッシュして、他の人が見られるようにリンクを張ってくれると助かります。 まとめ 繰り返しになりますが、私たちが行ったことをグラフィカルにまとめると、以下のようになります。 動的ルートを持つページを静的に生成する方法 まだ、ブログのマークダウンコンテンツを表示していません。次はこれをやってみましょう。 ### 5-6. Render Markdown ### 5-7. Polishing the Post Page ### 5-8. Polishing the Index Page ### 5-9. Dynamic Routes Details
その4 Pre-rendering and Data Fetching
用語
Pre-rendering
Data Fetching
- 文字のまま。データをフェッチするという意味。
- つまり、データを取り込む、どこかからデータをとってくるといった感じ。
4-1. Pre-rendering and Data Fetching
- 最終的にブログを作成したいが、現時点ではブログのコンテンツがない状態
- このレッスンでは、外部のブログデータをアプリに取り込む方法を学ぶ
- このチュートリアルでは、ブログのコンテンツをファイルシステムに格納する
- コンテンツが他の場所(データベースやヘッドレスCMSなど)に格納されている場合でも同様に実装できる
このレッスンで学ぶこと
- このレッスンでは、以下について学ぶ
- Next.jsの
pre-rendering
機能について - 二種類のpre-rendering方式:
Static Generation
とServer-side Rendering
- データありとデータなしの
Static-Generation
getStaticProps
を使用した、インデックスページへの外部ブログデータのインポート方法getStaticProps
に関するいくつかの有益な情報
- Next.jsの
4-2. Setup
- 前のレッスンから続けて受講する場合は、このページを読み飛ばすことができます。
4-3. Pre-rendering
データの取り込みについて話す前に、Next.jsの最も重要な概念のひとつである
Pre-rendering
について説明するデフォルトでは、Next.jsはすべてのページを事前レンダリングする
事前レンダリングとは
- クライアントサイドのJavaScriptですべてのHTML生成を行うのではなく、Next.jsがあらかじめ各ページのHTMLを生成しておくことを意味する
- パフォーマンスとSEOの向上につながります。
生成された各HTMLは、そのページに必要な最小限のJavaScriptコードと関連付けられている
- ブラウザがページを読み込むと、そのJavaScriptコードが実行され、ページが完全にインタラクティブになる
- このプロセスは、
Hydration
と呼ばれる
プリレンダリングが行われていることを確認する
プリレンダリングが行われているかどうかは、以下の手順で確認することができます。
- ブラウザのJavaScriptを無効にし(Chromeの場合はこちら)、...
- このページ(このチュートリアルの最終結果)にアクセスしてみてください。
JavaScriptなしでアプリがレンダリングされていることがわかるはずです。
- これはNext.jsがアプリを静的なHTMLにプリレンダリングしているためで、JavaScriptを実行せずにアプリのUIを見ることができます。
注意:上記の手順をlocalhostで試すこともできますが、JavaScriptを無効にするとCSSが読み込まれません。
- アプリがプレーンな React.js アプリ(Next.js を含まない)の場合、プリレンダリングがないため、JavaScript を無効にするとアプリが表示されなくなります。たとえば
- ブラウザのJavaScriptを有効にして、このページをご覧ください。これは、Create React Appで構築したプレーンなReact.jsのアプリです。
- ここで、JavaScriptを無効にして、もう一度同じページにアクセスしてみてください。
- アプリは表示されなくなり、代わりに "You need to enable JavaScript to run this app." と表示されます。これは、アプリが静的なHTMLにプリレンダリングされていないためです。
まとめ:プリレンダリングとプリレンダリングなしの比較
簡単に図式化すると、次のようになります。
- Next.jsありの場合
Initial Load: 事前読み込みされたHTMLが表示される
↓ JavaScriptファイル読み込み
Hydration: React要素が初期化され、アプリがインタラクティブになる
例えば、インタラクティブな要素である<Link />
があった場合、JSファイル読み込み後にアクティブになる
- Next.jsを使用しない素のReact.jsアプリの場合
Initial Load: 画面には何も描画されない
↓ JavaScriptファイル読み込み
Hydration: React要素が初期化され、アプリがインタラクティブになる(素のReactにはLinkコンポーネントはない)
4-4. Two Forms of Pre-rendering
- 事前レンダリングには次の2種類があります。
※留意事項
- 開発環境(
npm run dev
実行時)においては、開発しやすくするために①, ②に関わらず、リクエスト毎にページは事前レンダリングされる。 本番環境においては、①の方式を採用していれば、ビルド時に一度だけ事前レンダリングが行われる。
Next.jsでは、各ページに対して①, ②のどちらの方式を採用するか選択することができる
- 一般的な構成ではほとんどのページが①Static Generation、その他のページを②Server-side Renderingで実現される
- 一度ビルドすればCDNから提供され、各リクエストに対する応答がより高速であるということから可能な限り①を使うことが推奨される
①Static Generationが適している具体なページの例としては下記がある
- マーケティングページ
- ブログ投稿ページ
- Eコマース製品の一覧ページ
- ヘルプページやドキュメント
- 逆に、頻繁に更新されるデータを表示するページやリクエスト毎にコンテンツが変化するような場合は②を採用しましょう。
- 次のレッスンでは、①Static Generationに焦点を当てて、データを伴う場合、データを伴わない場合について解説していきます。
4-5. Static Generation with and without Data
- Static Generationはデータを伴う場合とデータを伴わない場合に分けて考えることができる
ここまでに作成したページは、外部データを取得しないもの
- このようなページではプロダクション環境向けにアプリをビルドした時に自動的に静的にHTMLが生成されることになります。
一方で、データ取得した後でなければ描画できないページも考えられる
- 例えば、ビルド時に、ファイルシステムや外部API、データベースなどからデータを取得するような場合だ
- Next.jsでは、データを伴うページの静的生成(Static Generation with data)にも対応できるようになっている
Static Generation with Data using getStaticProps
さて、データを伴うページの静的生成は、どのようにして動くのでしょうか?
- Next.jsでは、ページコンポーネントと一緒に
getStaticProps
という非同期関数(async function)もエクスポートすることで実現されます。
- Next.jsでは、ページコンポーネントと一緒に
動作としては、
getStaticProps
がプロダクション環境向けのビルド時に実行され- 関数内で、外部データをfetchして、そのデータをpropsとしてページに渡せます
export default function Home(props) { ... } export async function getStaticProps() { // ファイルシステム、API、DBなどからの外部データを取得する const data = ... // `props`をキーとする値が`Home`コンポーネントに渡される return { props: ... } }
[プログラムの要点]
- async functionとしてgetStaticPropsをexportして、その中で外部データを取得して、propsとしてリターンすると、Home
componentに渡すことができる
基本的に、
getStaticProps
はNext.jsに次のように伝えます。- 「おい、このページにはいくつかのデータ依存性があるぞ。だから、ビルド時にこのページをプリレンダリングするときは、最初にそれらを解決しておけよ!」。
注意: 開発モードでは、
getStaticProps
は各リクエストで実行されます。
getStaticPropsを使ってみよう
- 実際にやってみるとわかりやすいので、次のページからは
getStaticProps
を使ってブログを実装していきます。
4-6. Blog Data
シンプルなブログのアーキテクチャを作成する
マークダウンファイルの作成
- ルートフォルダに
nextjs-app/posts/
という新しいディレクトリを作成する(これはpages/posts
とは別物) nextjs-app/posts/
の中に、pre-rendering.md
とssg-ssr.md
という2つのファイルを作成する
さて、以下のコードをposts/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 let's 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.
そして、以下のコードをposts/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.
- Note
gray-matterのインストール
- まず、各マークダウンファイルのメタデータをパースするためのgray-matterをインストールする
npm install gray-matter
ファイルシステムを読み込むユーティリティ関数の作成
- ファイルシステムからデータをパースするための、次の処理を行えるユーティリティ関数を作成する
import fs from 'fs'; // Note: ファイルシステムからファイルを読み込むためのモジュール import path from 'path'; // Note: ファイルのパスを操作するためのモジュール import matter from 'gray-matter'; // Note: マークダウンファイルのメタデータをパースするためのライブラリ const postsDirectory = path.join(process.cwd(), 'posts'); export function getSortedPostsData() { // ディレクトリ"/posts"配下のファイル名取得する const fileNames = fs.readdirSync(postsDirectory); const allPostsData = fileNames.map((fileName) => { // ファイル名をidとして扱うため、不要となる拡張子".md"をファイル名から削除する(正規表現を使用している) 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
) に追加する必要がある - これを行うには、Next.jsのデータ取得メソッドである
getStaticProps()
を使用する - 次のセクションでは、
getStaticProps()
の実装方法について学びます。
4-6 まとめ
- DBの位置付けとしてこのチュートリアルではマークダウンファイルを用意した
- これらのファイルは、メタデータ(日付、タイトル)を含んでいる
- マークダウン(のメタデータ部分)を解析するために
gray-matter
をnpm install
した - 解析処理を行うための関数
getSortedPostsData
をエクスポートした ここまでのステップでマークダウンファイルから
pages/index.js
に掲載するidとデータを紐付けた内部データを扱えるようになった次のステップでは、Next.jsのデータ取得関数
getStaticProps()
を利用する方法を学習する
4-7. Implement getStaticProps
Next.jsのpre-rendering(事前レンダリング)には二通りあり、これらの違いは"HTMLがいつ生成されるか"にある
① Static Generation (静的生成)
- HTMLをビルド時に生成する方式で、リクエスト毎に再利用される
② Server-side Rendering (サーバーサイドレンダリング)
- HTMLを各リクエスト毎に毎回生成する方式
※ Next.jsにおいては、多くのページを①で実現し、その他残りのページを②で実現することで描画の効率を高めている
静的生成(getStaticProps()
)を利用する
- さて、
getSortedPostsData
のimportを追加して、pages/index.js
のgetStaticProps
の中で呼び出す必要があります。 pages/index.js
をエディタで開き、エクスポートされたHome
コンポーネントの上に以下のコードを追加します。
import { getSortedPostsData } from '../lib/posts'; export async function getStaticProps() { const allPostsData = getSortedPostsData(); return { props: { allPostsData, }, }; }
getStaticProps
でprops
オブジェクトの中にallPostsData
を返すことで、ブログの記事がpropとしてHomeコンポーネントに渡されることになります。- これで、以下のようにブログ記事にアクセスすることができます。
export default function Home ({ allPostsData }) { ... }
- ブログ記事を表示するために、Homeコンポーネントを更新し、自己紹介のあるセクションの下に、データを含む別の
タグを追加しましょう。 props
を()
から({ allPostsData })
に変更することを忘れないでください。
export default function Home({ allPostsData }) { return ( <Layout home> {/* Keep the existing code here */} {/* Add this <section> tag below the existing <section> tag */} <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> ); }
これで、
http://localhost:3000
にアクセスすれば、ブログデータが表示されるはずです。おめでとうございます。外部データを(ファイルシステムから)取得し、このデータでインデックスページをプリレンダリングすることに成功しました。
getStaticProps()
Markdownファイルの解析
↓
ページコンポーネントにpropsとして投稿データの配列を渡す
↓
インデックスページにブログ投稿のリストを表示する
- 次のページでは、getStaticPropsの使い方のコツについて説明します。
4-8. getStaticProps Details
getStaticPropsの詳細
- getStaticPropsについて知っておくべき重要な情報の紹介
外部APIやデータベースを取得する
lib/posts.js
では、ファイルシステムからデータを取得するgetSortedPostsData
を実装しています。- しかし、あなたは外部APIエンドポイントのような他のソースからデータをフェッチすることができ、それはうまく動作します。
export async function getSortedPostsData() { // Instead of the file system, // fetch post data from an external API endpoint const res = await fetch('..'); return res.json(); }
注意:Next.jsはクライアントとサーバーの両方でfetch()をポリフィルしています。インポートする必要はありません。
- また、直接データベースに問い合わせることもできます。
import someDatabaseSDK from 'someDatabaseSDK' const databaseClient = someDatabaseSDK.createClient(...) export async function getSortedPostsData() { // Instead of the file system, // fetch post data from a database return databaseClient.query('SELECT posts...') }
- これは、
getStaticProps
がサーバーサイドでのみ実行されるためです。 - クライアントサイドでは決して実行されません。ブラウザ用のJSバンドルに含まれることもありません。
- つまり、データベースへの直接問い合わせのようなコードを、ブラウザに送信することなく書くことができるのです。
開発環境と本番環境
- 開発環境(
npm run dev
またはyarn dev
)では、getStaticProps
はすべてのリクエストで実行されます。 - 実運用環境では、
getStaticProps
はビルド時に実行されます。 - しかし、この動作は
getStaticPaths
が返すフォールバックキーを使って拡張することができます。 - これはビルド時に実行されるものなので、 クエリパラメータや HTTP ヘッダのようなリクエスト時にしか使用できないデータは使用できません。
ページ内でのみ許可される
getStaticPropsは、ページからしかエクスポートできません。ページ以外のファイルから書き出すことはできません。
この制限の理由の1つは、Reactはページがレンダリングされる前に必要なデータをすべて持っている必要があるためです。
リクエスト時にデータを取得する必要がある場合はどうすればよいですか?
静的生成はビルド時に一度だけ行われるため、頻繁に更新されるデータや、ユーザーのリクエストごとに変更されるデータには適していません。
このような、データが変化する可能性がある場合は、サーバーサイド・レンダリングを使用することができます。
- サーバーサイド・レンダリングについては、次のセクションで詳しく説明します。
4-8 まとめ
getStaticProps
がサーバサイドで実行されるため、ファイルシステムやデータベースからの情報の読み込みを実現できる- 開発環境では、
getStaticProps
はリクエスト毎に実行されるが、本番環境ではビルド時のみ実行される(ただし、fallback
キーを使って拡張可能) getStaticProps
はページファイルからのみエクスポートできる- 頻繁に更新が必要なページにはこの手法は向いていないため、以降に紹介される
Server-sdie Rendering
を利用する- 言い換えると、ビルド時ではなく、リクエスト時にデータの取得の必要があるならば
Server-sdie Rendering
を利用すべき
- 言い換えると、ビルド時ではなく、リクエスト時にデータの取得の必要があるならば
4-9. Fetching Data at Request Time
ビルド時ではなく、リクエスト時にデータを取得する必要がある場合は、サーバーサイドレンダリングを試すことができます。
サーバーサイド・レンダリングを使用するには、ページからgetStaticPropsの代わりにgetServerSidePropsをエクスポートする必要があります。
getServerSidePropsの使用法
getServerSidePropsのスターターコードです。このブログの例では必要ないので、実装しないことにします。
export async function getServerSideProps(context) { return { props: { // props for your component }, }; }
getServerSidePropsはリクエスト時に呼び出されるため、そのパラメータ(context)にはリクエスト固有のパラメータが含まれます。
getServerSideProps は、リクエスト時にデータを取得しなければならないページをプリレンダリングする必要がある場合にのみ、使用する必要があります。TTFB (Time to First Byte) は getStaticProps よりも遅くなります。なぜなら、サーバーはリクエストごとに結果を計算しなければならず、その結果は特別な設定なしにCDNによってキャッシュされることができないからです。
クライアントサイドレンダリング
データをプリレンダリングする必要がない場合は、次の戦略(Client-side Renderingと呼ばれる)を使用することもできます。
外部データを必要としないページの部分を静的に生成(プリレンダリング)します。 ページが読み込まれたら、JavaScriptを使用してクライアントから外部データを取得し、残りの部分にデータを入力します。
この方法は、たとえば、ユーザーのダッシュボード・ページで有効です。ダッシュボードはプライベートなユーザー固有のページなので、SEOは関係なく、ページがプリレンダリングされる必要もありません。データは頻繁に更新されるため、リクエスト時にデータを取得する必要があります。
SWR
Next.jsの開発チームは、SWRと呼ばれるデータフェッチ用のReactフックを作成しました。クライアントサイドでデータを取得する場合は、これを強くお勧めします。SWRは、キャッシュ、再バリデーション、フォーカストラッキング、インターバルでのリフェッチなどを処理します。ここでは詳細を説明しませんが、使用例を示します。
import useSWR from 'swr'; function Profile() { const { data, error } = useSWR('/api/user', fetch); if (error) return <div>failed to load</div>; if (!data) return <div>loading...</div>; return <div>hello {data.name}!</div>; }
詳しくは、SWRのドキュメントをご覧ください。
以上です。 次のレッスンでは、ダイナミックルートを使って各ブログ投稿のページを作成します。
MUIのTypographyでhrefを使おうとしたら"この呼び出しに一致するオーバーロードはありません。"が出て詰まりかけた
タイトルの通りです。
発生状況
Next.jsにMUIを導入し、ResponsiveAppBarのサンプルコードをベースとして、AppBarのボタンにページ遷移用リンクを付与しようとした時に発生した。
補足: 正確にはボタンというよりレスポンシブデザインで画面が小さい場合に左上の「三」から選択できるメニュー
結論
- 上に書いた状況はあまり関係ないが、単純に
Typography
の使い方が間違っていた。 Typography
でhref
を使うときは、component="a"
も同時に定義しないといけなかった。
エラーが出たプログラム
const pageItems = [ { pageTitle: 'About', pageUrl: '/about' } ]; {pageItems.map((pageItem) => { const {pageTitle, pageUrl} = pageItem; return ( <MenuItem key={pageTitle} onClick={handleCloseNavMenu}> <Typography href={pageUrl} textAlign="center" > {pageTitle} </Typography> </MenuItem> ); })}
修正したプログラム
const pageItems = [ { pageTitle: 'About', pageUrl: '/about' } ]; {pageItems.map((pageItem) => { const {pageTitle, pageUrl} = pageItem; return ( <MenuItem key={pageTitle} onClick={handleCloseNavMenu}> <Typography component="a" // これが必要だった href={pageUrl} textAlign="center" > {pageTitle} </Typography> </MenuItem> ); })}
DockerでNode/Nginx/Janusを単体で動かす。NginxでJanusのデモページをホスティングする。
Node.js
Dockerfile作成
FROM node:16.16.0-alpine WORKDIR /usr/src/app RUN apk update && apk add bash CMD ["/bin/bash"]
ビルド: 上で作成したDockerfileからイメージを作成する
$ docker image build -t node_img:latest . 👇 $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE node_img latest 13651deec3a6 4 days ago 117MB
コンテナ起動: 上で作成したイメージからコンテナを作成し、起動する
$ docker run --name node_cnt -it node_img bash-5.1# bash-5.1# pwd /usr/src/app bash-5.1# exit exit 👇 $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6e198779aa4e node_img "docker-entrypoint.s…" 17 seconds ago Exited (0) 9 seconds ago node_cnt
以上で、docker上でNode.js(bashも使える)環境が整う。
Nginx
Dockerfileの作成
FROM nginx CMD ["nginx", "-g", "daemon off;"]
ビルド: 上で作成したDockerfileからイメージを作成する
$ docker image build -t nginx_img:latest . 👇 $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx_img latest 53685b45547d 2 weeks ago 142MB
コンテナ起動: 上で作成したイメージからコンテナを作成し、起動する
$ docker run --name nginx_cnt -d -p 8080:80 nginx_img:latest 571c9fb70e7ad4632384b97c6f4e7cd14dfc1a43887a5966ddd8656a25f6cdb1 👇 $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 571c9fb70e7a nginx_img:latest "/docker-entrypoint.…" 5 minutes ago Up 5 minutes 0.0.0.0:8080->80/tcp nginx_cnt
以上で、docker上でNginx(ブラウザでhttp://localhost:8080/
にアクセスするとnginxが起動している)環境が整う。
Janus
Dockerfileの作成
FROM ubuntu:20.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt update RUN apt install -y sudo make wget git python3-pip RUN apt install -y \ libmicrohttpd-dev libjansson-dev \ libssl-dev libsofia-sip-ua-dev libglib2.0-dev \ libopus-dev libogg-dev libcurl4-openssl-dev liblua5.3-dev \ libconfig-dev pkg-config libtool automake # libnice RUN pip3 install meson ninja && \ cd /root && \ git clone https://gitlab.freedesktop.org/libnice/libnice && \ cd libnice && \ meson --prefix=/usr build && ninja -C build && sudo ninja -C build install # libsrtp RUN cd /root && \ wget https://github.com/cisco/libsrtp/archive/v2.2.0.tar.gz && \ tar xfv v2.2.0.tar.gz && \ cd libsrtp-2.2.0 && \ ./configure --prefix=/usr --enable-openssl && \ make shared_library && sudo make install # janus-gateway RUN cd /root && \ git clone https://github.com/meetecho/janus-gateway.git && \ cd janus-gateway && \ sh autogen.sh && \ ./configure --prefix=/opt/janus && \ make && make install && make configs ENTRYPOINT ["/opt/janus/bin/janus"]
ビルド: 上で作成したDockerfileからイメージを作成する
$ docker image build -t janus_img:latest . 👇 $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE janus_img latest 36355143bf20 3 minutes ago 819MB
コンテナ起動: 上で作成したイメージからコンテナを作成し、起動する
$ docker run --name janus_cnt -d -p 8088:8088 janus_img:latest 073bb8e0a550ca4216573504760026d5afa1c396becf030e8d3d685545d83b26 👇 $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 073bb8e0a550 janus_img:latest "/opt/janus/bin/janus" 8 seconds ago Up 7 seconds 0.0.0.0:8088->8088/tcp janus_cnt
以上で、docker上でJanus(ブラウザでhttp://localhost:8088/janus/info
にアクセスするとJSONが返ってくる)環境が整う。
NginxでJanusのデモページをホスティングする
前提: NginxのDockerfileと同じ階層にhtml/videoroomtest.html
を配置しておく
JanusとNginxを起動
# Nginxを起動 $ docker run -v `pwd`/html:/usr/share/nginx/html --name nginx_cnt_demo -d -p 8080:80 nginx_img:latest 8b95ca497f08cd7db797354693ef11821add4a599800aa6e51e65e1a32dfbeec # Janusを起動(デモページのStartボタンが機能するようになる) $ docker run --name janus_cnt_demo -d -p 8088:8088 janus_img:latest 7a11a39c371a067e290f6750d5eda9cb96f1a2ab910c7bdcbe1c23bbba337749 👇 $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7a11a39c371a janus_img:latest "/opt/janus/bin/janus" About a minute ago Up 59 seconds 0.0.0.0:8088->8088/tcp janus_cnt_demo 8b95ca497f08 nginx_img:latest "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:8080->80/tcp nginx_cnt_demo
以上で、docker上でNginxでJanusのデモページ(http://localhost:8080/videoroomtest.html
)を動かす環境が整う。
Dockerビルド時のtオプションについて
-t
オプション
-t
オプションを利用すると、イメージ名とタグ名を指定できるイメージ名は
REPOSITORY
列、タグ名はTAG
列に表示される名前のこと$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE
-t
オプションを指定しない場合、次のようにイメージ名とタグ名に<none>
が設定される<none>
のままだと管理が大変になるため通常は-t
オプションを付けるべき
$ docker image build . $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE <none> <none> 6ee309c7e004 34 minutes ago 112MB
-t hoge
とすると、イメージ名がhoge
となり、タグ名は自動的にlatest
に設定される$ docker image build -t hoge . $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE hoge latest 6ee309c7e004 36 minutes ago 112MB
-t hoge:latest
とすると、イメージ名をhoge
に、タグ名を明示的にlatest
に設定できる$ docker image build -t hoge:latest . $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE hoge latest 6ee309c7e004 38 minutes ago 112MB
-t hoge:v0.1
とした場合、イメージ名がhoge
で、タグ名はv0.1
になる$ docker image build -t hoge:v0.1 . $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE hoge v0.1 6ee309c7e004 42 minutes ago 112MB
Wikiネットワーク?的なものを考えた
少し前から思っているこんなのあったらどうかなってやつ。走り書きメモなので適当です。
自分の知っている分野の外側を知ることって難しいと思っている。
まだ知らないだけで、本当は自分の好奇心を揺さぶるようなことがあるかもしれない。
でもどうやったらそこに到達できるか?これはなかなか難しいのではと感じる。
人間は基本的に言葉を使って思考するのでこれを利用できないか。
そこで思いついたのが、Word2Vecとかなんとかを使って、ネットワークを球体のような感じで表示する。
各ノードはそれに関連したトピックス。
最初の案は自分で言葉をと登録していく方式で、何らかの辞書で関連するノードを計算し近くに表示する。 でも登録するのはめんどくさそうなので、とりあえずwikipediaとかの単語を拾ってきて適当なものを表示する。
これで何がいいかというと、あるキーワードから初めてそれ関連のものを知れるし、球体の反対側、 最初日本にいたとしたら、くるくるっと回してブラジルに位置にある単語は、最初のキーワードから反対(?)の言葉になる。
ってことで自分の知っている世界と反対の世界を知れるって感じで。
役に立つかは分からないけど、なんか作って見てもおもしろそーだなと。