猫でもわかるWeb開発・プログラミング

本業エンジニアリングマネージャー。副業Webエンジニア。Web開発のヒントや、副業、日常生活のことを書きます。

Next.js で React の Server-side Rendering と Static Generation をやる #2

前回の記事: Docker で Next.js 開発環境を用意する

前回は

  • Next.js の Server-side Rendering と Static Generation についての説明
  • Docker で Next.js の開発環境を用意

までやりました。まだ読んでいない人はこちら。

www.utakata.work

今回の概要

今回は適当なページを追加して、Next.js の基本的な文法や、使い方を学びます。

React と違う部分も結構あるので、そこを中心にやっていきます。

Next.js チュートリアルのページ

今回も、Next.js のチュートリアルのページをベースにすすめていきます。

今回はここからやっていきます。

nextjs.org

ソースコード

作ったものは以下で公開しています

github.com

新しいページを追加する

Next.js のプロジェクトを作成すると、 pages/index.js というファイルが存在していますが、ここに、pages/posts/first-post.js を追加することで、ページを追加します。

https://127.0.0.1:3000/posts/first-post にアクセスすると、このページを表示するようにしたいです。

posts ディレクトリを作成して、first-post.js を作成します。

export default function FirstPost() {
  return <h1>First Post</h1>
}

ファイルを作るだけで、 /posts/first-post にアクセスできるようになっているはずです。

http://127.0.0.1:3000/posts/first-post

ページ遷移(リンク)

index.js から first-post.js へのリンクを貼ります。

index.js にて

# import を追加して
import Link from 'next/link'

# 適当なところにリンクを貼る

        <h1 className="title">
          Read{' '}
          <Link href="/posts/first-post">
            <a>this page</a>
          </Link>
        </h1>

最初 Read{' '} ってなにかと思ったんだけど、これは Read{' '} に分かれます。

「Read this page」と表示したいんだけど、 Read と this の間にスペース開けるために {' '} が必要らしい。 {' '} が無いと Readthis page となってしまいます。

さらに、 /posts/first-post から index.js に戻れるようにします。

import Link from 'next/link'

export default function FirstPost() {
    return (
        <>
            <h1>First Post</h1>
            <h2>
                <Link href="/">
                    <a>Back to home</a>
                </Link>
            </h2>
        </>
    )
}

ちなみに、これは Client-Side Navigation というものになります。

  • 実際にはページ移動をしているわけではない
  • JSでページ遷移している

また、Code splitting and prefetching という仕組みが動いていて、

  • ページに必要な情報だけ読まれる。ページにアクセスした時にすべてのページからサーバーから渡されるわけではない。
  • 本番用のビルドだと、Link は自動的にプリフェッチされる

画像などのアセットの配置方法

画像とかは public ディレクトリに置きます。 index.js のフッターにある通り、<img> タグで参照できます。

<img src="/vercel.svg" alt="Vercel Logo" className="logo" />

title などのメタデータの設定方法

<title> タグなどは <Head> の中に書きます。 index.jsにある通りです。

<Head>
  <title>Create Next App</title>
  <link rel="icon" href="/favicon.ico" />
</Head>

FirstPost にも Head を追加してみます。

import Head from 'next/head'
import Link from 'next/link'

export default function FirstPost() {
    return (
        <>
            <Head>
                <title>First Post</title>
            </Head>
            <h1>First Post</h1>
            <h2>
                <Link href="/">
                    <a>Back to home</a>
                </Link>
            </h2>
        </>
    )
}

CSSの書き方

index.js にはすでに CSS があたっています。

<style jsx>{`
  …
`}</style>

これは styled-jsx ってやつで、CSS in JS というやつです。 他にも styled-components とか emotion みたいな有名な CSS in JS ライブラリに対応しています。

他にも、Build-in CSS というのもあるし、 Tailwind CSS みたいなメジャーな CSS ライブラリには対応しています。

Layout

レイアウトというのを設定できます。すべてのページにヘッダを表示するみたいな、よくあるレイアウト機能です。

components というディレクトリを作成し、その中に layout.js を作成します。

export default function Layout({ children }) {
  return <div>{children}</div>
}

first-post からこのレイアウトを使ってみる。全体を <Layout> で囲う。

import Head from 'next/head'
import Link from 'next/link'
import Layout from '../../components/layout'

export default function FirstPost() {
    return (
        <Layout>
            ...
            <h1>First Post</h1>
            <h2>
                <Link href="/">
                    <a>Back to home</a>
                </Link>
            </h2>
        </Layout>
    )
}

さらに、 components/layout.module.css を作成します。

.container {
    max-width: 36rem;
    padding: 0 1rem;
    margin: 3rem auto 6rem;
}

で、components/layout.js を編集します。

import styles from './layout.module.css'

export default function Layout({ children }) {
  return <div className={styles.container}>{children}</div>
}

これでCSSが適用されたはずです。

ここまでの変更内容: https://github.com/yoshikyoto/nextjs-blog/commit/9890a498fd3b411f0206e5196657bef0585f248d

グローバルにCSSを適用させる

CSSは基本コンポーネントごとになっちゃうので、グローバルに適用させるためには、まず、pages/_app.js を作成します。

export default function App({ Component, pageProps }) {
  return <Component {...pageProps} />
}

作成したらアプリの再起動が必要なので、dockerを再起動させます。

docker-compose down
docker-compose up -d

styles/global.css を作成します。

html,
body {
  padding: 0;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu,
    Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  line-height: 1.6;
  font-size: 18px;
}

* {
  box-sizing: border-box;
}

a {
  color: #0070f3;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

img {
  max-width: 100%;
  display: block;
}

pages/_app.js で、↑のCSSを読み込ませます。以下を追記するだけです。

import '../styles/global.css'

分かりづらいので、適当に a タグの color とか変えてみて、色が変わることを確認すると良いでしょう。

ここまでの差分: https://github.com/yoshikyoto/nextjs-blog/commit/cf46f8457b5b5fc9561412b8e0be10ffd3530934

ページをさらに作りこんでいく

Next.js だと以下のページに該当する。

https://nextjs.org/learn/basics/assets-metadata-css/polishing-layout

レイアウトの洗練、いらなくない?と思ったけど、後で出てくる Pre-rendering の部分でこのコード使うので、やっておいたほうがいい。

適当に.jpgのプロフィール画像を用意しておきます。400x400くらいが推奨らしいです。

public/imagesディレクトリを作って、その中に入れます。

この章は変更が大きいので、私の GitHub を見ると良い

https://github.com/yoshikyoto/nextjs-blog/commit/5e2085f3648bebbeacd6cfd7630f5c8a85bf43bd

変更部分

  • layout.module.css
  • styles/utils.module.css
    • ちなみに、styles の中には global.cssが今は入っている
  • components/layout.js
    • utils.module.css を新たに import するようにする
    • const siteTitle をここに定義するのかー
  • pages/index.js

んで、こうなる。read this page をクリックしても、ヘッダには常に Utakata があるようなレイアウトに。

f:id:yoshiki_utakata:20210205161653p:plain

CSSに関するTips

Styling について、どれがベストかは、色々使ってみて試すしかない。

次回: Pre-rendering

次回はついに Pre-rendering (SSR, Static Generation)をやります。

www.utakata.work