React を使ってプロトタイプを実装したりすることはあるけど,今まで Next.js を試したことがなかった.そろそろ入門しなければ!という危機感もあり,Next.js Learn (Basic) を一通り試してみた.入門する前と比較すると「Next.js でできること(基礎の基礎)」を把握することができ,非常に良質なコンテンツに仕上がっていた.今回は Next.js Learn (Basic) から学んだことを整理する.Next.js に入門するならまず試してみると良いと思う.
なお,今回は Next.js 9.2.1 を使えるようにローカル環境を構築した.
$ npm view next version 9.2.1
Next.js Learn (Basic)
Basic では「計9種類」のコンテンツが用意されている.Next.js を使ったサイト内遷移(ルーティング)もあれば,共通コンポーネントの設計もある.ページを静的に最適化したり,styled-jsx
を使った CSS-in-JS を実現したり,Now を使ったデプロイも学べる.SSR (Server-Side Rendering) 以外の機能も多く試せる.素晴らしい!
- Getting Started
- Navigate Between Pages
- Using Shared Components
- Create Dynamic Pages
- Clean URLs with Dynamic Routing
- Fetching Data for Pages
- Styling Components
- API Routes
- Deploying a Next.js App
また,手順通りに進めるだけではなく,以下のように動作確認をしながら結果を選ぶクイズも出る.写経をしながら進める価値もあるし,理解度も高まるし,よく考えられているなと思う.
Next.js アプリケーション
Next.js Learn (Basic) のコンテンツは全て繋がっている.ステップバイステップに学ぶことができるし,コンテンツごとに実装する Next.js アプリケーションも異なる.具体的には,以下の「計4種類」の Next.js アプリケーションを実装する.楽しいぞ!
- Hello World
- Blog
- TV Shows(番組表)
- Famous Quotes(名言集)
参考までに「Blog アプリ」と「TV Shows アプリ」のキャプチャを載せておく.
コードは GitHub に公開されている.
HMR (Hot Module Replacement)
Next.js では,開発環境だとデフォルトで HMR (Hot Module Replacement) が使える.コードを修正すると,すぐにページに反映されるため,実装時のストレスがなく,便利だった.
Link
コンポーネント
Next.js では pages
ディレクトリ直下に JavaScript ファイルを置くと,自動的に認識される.例えば pages/about.js
を作成すれば http://localhost:3000/about
にアクセスできるようになる.そして,サイト内遷移を実現するときには Link
コンポーネントを使う.今回は「Hello World アプリ」で以下のようなコードを実装した.react-router-dom
などを使わずに Next.js で書けるのは良かった.
import Link from 'next/link'; export default function Index() { return ( <div> <Link href="/about"> <a title="About Page">About Page</a> </Link> <p>Hello Next.js</p> </div> ); }
Create Dynamic Pages
「Blog アプリ」で2種類の「サイト内遷移」を実装する.最初は以下のような実装となり,Link
コンポーネントを使って href
props にクエリストリングを追加している.
import Layout from '../components/MyLayout.js'; import Link from 'next/link'; const PostLink = props => ( <li> <Link href={`/post?title=${props.title}`}> <a>{props.title}</a> </Link> </li> ); export default function Blog() { return ( <Layout> <h1>My Blog</h1> <ul> <PostLink title="Hello Next.js" /> <PostLink title="Learn Next.js is awesome" /> <PostLink title="Deploy apps with Zeit" /> </ul> </Layout> ); }
遷移したページでは以下のような実装となり,useRouter
フックを使ってクエリストリングを取得している.
import { useRouter } from 'next/router' import Layout from '../components/MyLayout.js' const Page = () => { const router = useRouter() return ( <Layout> <h1>{router.query.title}</h1> <p>This is the blog post content.</p> </Layout> ) } export default Page
ただし,この実装だと URL は http://localhost:3000/post?title=Hello%20Next.js
となり,課題が残る.そこで,Next.js の「Dynamic Routes」機能を使う.以下のような実装となり,href
props は /p/[id]
のように []
を使った記法になる.また as
を使って [id]
の値を「動的に」設定する.
import Layout from '../components/MyLayout.js' import Link from 'next/link' const PostLink = props => ( <li> <Link href="/p/[id]" as={`/p/${props.id}`}> <a>{props.id}</a> </Link> </li> ) export default function Blog() { return ( <Layout> <h1>My Blog</h1> <ul> <PostLink id="hello-nextjs" /> <PostLink id="learn-nextjs" /> <PostLink id="deploy-nextjs" /> </ul> </Layout> ) }
遷移したページの実装はあまり変わらないけど,ファイルパスが pages/p/[id].js
となり,[]
をそのままファイル名に使う点は新しく感じた.URL は http://localhost:3000/p/hello-nextjs
となる.
pages ├── about.js ├── index.js └── p └── [id].js
getInitialProps
を使う
「TV Shows アプリ」を実装するときに,TVmaze API を使ってテレビ番組データを取得する.そのため,初回ロード時に API を実行する必要があり,今回は getInitialProps
を使った.例えば,以下のように実装すると,Index
コンポーネントの props から shows
を取得できるようになる.
Index.getInitialProps = async function() { const res = await fetch('https://api.tvmaze.com/search/shows?q=batman'); const data = await res.json(); console.log(`Show data fetched. Count: ${data.length}`); return { shows: data.map(entry => entry.show) }; };
次にテレビ番組データを表示するページの実装をする.以下の実装に console.log
があり,このログ出力を動作確認する手順は個人的には気付きが多かった.この console.log
のログは「ブラウザ側」と「サーバ側」のどちらに出力されるのか?
import Layout from '../../components/MyLayout'; import fetch from 'isomorphic-unfetch'; const Post = props => ( <Layout> <h1>{props.show.name}</h1> <p>{props.show.summary.replace(/<[/]?[pb]>/g, '')}</p> {props.show.image ? <img src={props.show.image.medium} /> : null} </Layout> ); Post.getInitialProps = async function(context) { const { id } = context.query; const res = await fetch(`https://api.tvmaze.com/shows/${id}`); const show = await res.json(); console.log(`Fetched show: ${show.name}`); return { show }; }; export default Post;
結果としては,トップページから画面遷移をすると「ブラウザ側」にログが表示される.そして http://localhost:3000/p/481
のように URL を直接開くと「サーバ側」にログが表示される.この挙動こそ SSR だよなー!と改めて気づけて良かった.
まとめ
- Next.js に入門するために Next.js Learn (Basic) を一通り試した
- Next.js でできることを把握することができる
- 次は Next.js Learn (Excel) に進むぞ!学ぶことが無限にある!
コンテンツとステップ
最後は付録として,コンテンツごとにステップをまとめておく.多くあるように見えるけど,実際に写経(とコピペ)をしながら進めるだけなら「2,3時間」あれば十分に終わると思う.参考までー!
- Getting Started
- Introduction
- Setup
- 404 Page
- Creating Our First Page
- Handling Errors
- You are Awesome
- Navigate Between Pages
- Introduction
- Setup
- Using Link
- Client-Side History Support
- Adding Link Props
- Link is Just a Wrapper Component
- Link is Simple, but Powerful
- Using Shared Components
- Introduction
- Setup
- Create the Header Component
- Using the Header Component
- The Component Directory
- The Layout Component
- Rendering Child Components
- Using Components
- Create Dynamic Pages
- Introduction
- Setup
- Adding a list of posts
- Passing Data via Query Strings
- useRouter
- Finally
- Clean URLs with Dynamic Routing
- Introduction
- Setup
- Dynamic Routing
- History Awareness
- Finally
- Fetching Data for Pages
- Introduction
- Setup
- Fetching Batman Shows
- Only on the Server
- Implement the Post Page
- Fetch Data in Client Side
- Finally
- Styling Components
- Introduction
- Setup
- Styling our home page
- Styles should go inside template strings
- Styles and Nested Components
- No Effect for Nested Component
- Global Styles
- Global Styles Work
- What Next
- API Routes
- Introduction
- Setup
- Creating an API route
- Fetching API Routes
- Middlewares
- Finally
- Deploying a Next.js App
- Introduction
- Setup
- Deploying to ▲ZEIT Now
- Deploying to Your Own Environment
- Build and Start
- Run two instances
- Build Once, Run Many Instances
- Finally