Open27

nextjs を掘る

takewelltakewell

https://nextjs.org/docs/getting-started

https://github.com/vercel/next.js/tree/canary/examples オフィシャルのボイラープレート集。パッケージを入れたい時などは参考になる。

雑にまとめると
/pages に react のコンポーネントをページ名のファイルで宣言して default export で宣言すればいい感じにマッピングしてくれる
/public においておけば静的なファイルサーバーとしてホスティングしてくれる マッピングは /

以上, webpack とか express とかで一から構築すること比較すると楽すぎて泣けてくる

takewelltakewell

https://nextjs.org/docs/basic-features/pages

ダイナミックルーティング
例えば商品ページなら /pages/goods/[goodsID].tsx とかでダイナミックルーティングしてくれる

プリレンダリングについて、いきなり肝の部分きた!

  • SSG と SSR できる レコメンドは SSG (まあ、そっちの方が比較的早いしキャッシュ効くしね...)

Importantly, Next.js lets you choose which pre-rendering form you'd like 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.

これはなかなかにスーパー機能... 商品ページは SSR, そんなダイナミックでないページは SSG って感じで使い分ければ、SSG のページは爆速いけそう。SSR は最適化次第か

SSG

Scenario 1: Your page content depends on external data

export async function getStaticProps これはビルド時に呼び出される、従って SSG になる。

Scenario 2: Your page paths depend on external data

export async function getStaticPaths これのコールバックが export async function getStaticProps({{params}} になる

ユーザーの要求によって、ページにレンダリングする内容が異なるか、否かがSSG の使い所、
もし、ユーザーのリクエストに応じてページを変えたい場合は二つの選択肢がある。

CSR するか SSR するか。

  • CSR 普通に ブラウザで非同期にリクエスト飛ばせばよい, SEO 問題 => これも2021 においては解消されてるか??、ユーザーの CPU パワーに速度が依存するのがデメリットかもだが、今時の端末ならそんな心配はいらないか...)
  • SSR はキャッシュできない. (というか、逆にキャッシュできるのならば、そもそも SSR にしてるのはおかしい。リアルタイムにしたい場合は、ユーザー依存の場合は CSR でよさそう。SEO 関係ないし)

SSR

getServerSideProps() のコールバックが export default const Page = ({data})

なだけ。

getStatic なら SSG getServer なら SSR で自動でビルド時に判別してくれるフレームワークっぽいところはこれだけ。めっちゃシンプルや!

所感

ほとんど SSGでできそう。ただし例えば EC とかの場合 トップ、商品詳細 は SSR がよさそう。SEO クソ大事でリアルタム性がシビアだと思うので, ページそのものをキャッシュさせるのは危ない。検索結果とかは CSR でいいと思うが、SEOに懸念がある場合はページ内遷移でない場合だけ SSR でそのほかは SPA 的に CSR させればよさそう。
ISR の解説がないけど、別ページか?

takewelltakewell

https://nextjs.org/docs/basic-features/data-fetching

next メソッドは 型をくれるっぽい!最高!(まあ当たり前か...)

import { GetStaticProps } from 'next'
import { InferGetStaticPropsType } from 'next'

Incremental Static Regeneration

ISR きた!
next 9.5 から入った機能でわりと最近だった。

この機能は要は、バッチ処理のすごいやつ(多分)

着想としては ブラウザの Cache Controlにおけるstale-while-revalidateg らしい、
これは定期間をキャッシュからレスポンスするが指定時間を経過したら非同期でオリジナルをfetchしてキャッシュをレスポンス/更新するというもの。らしい。
これは CDN によっては対応してたりしなかったりするらしい... ソースを確かめてはないが、ブログれべるでは fastly Cloudflare は対応, clond front は対応してないとのこと

一定間隔でバックグラウンドでページを静的に生成して、動的なコンテンツもユーザーにはスタティックなサイト(早い)をとして見せる
revalidate でその間隔は seconds で指定できる

どこまで極限までサイトを早くしたいんだ(呆れ)

ISR https://qiita.com/thesugar/items/47ec3d243d00ddd0b4ed

export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  return {
    props: {
      posts,
    },
    // Next.js will attempt to re-generate the page:
    // - When a request comes in
    // - At most once every second
    revalidate: 1, // In seconds
  }
}

この秒間をどうやって決めるか、もちろんコンテンツの更新頻度に依存し増だが、どのユーザーはページを見てる時にはデータは切り替わらないのだから 60 秒とかでいいよな気もする。この間隔を狭めると計算リソースをもちろん使うので、負荷との兼ね合いでもあるか。

以下 ISR について理解を助ける記事
https://qiita.com/thesugar/items/47ec3d243d00ddd0b4ed#時間の経過に応じて変化するデータへの対応

以下ISR のインフラに関する日本語記事 しかし.今のところ vercel 以外では ISR させるのはキツそう...

serverless-next.js ならば lamda@edge でいけるらしい... しかし、これは沼の可能性が高そう。

メソッドが詳しく解説されてる。preview モードとかあるのはよい。

https://swr.vercel.app/ の宣伝。まあSWR でよさそう

Reading files: Use process.cwd()

ファイルシステムにも普通に node の api で呼べる。

例えば md で管理してるものを html で描画したい場合などに使うっぽい
そのほかは普通に node でモジュールとして呼べばいいと思うので、乱用厳禁そう

export async function getServerSideProps(context) {

基本ここはがサーバーのエントリーポイントになる。いわゆるサーバーサイドの実装になる部分。
param, req, res, query, preview, locales 諸々必要なものが引数に入っている
バックエンドにサーバーがあるならあまりここでドメインロジックを書くのはやめたほうがよさそう。(書きたくなりそうだけど...)

所感

ここのページは nextjs の一番根幹のところだから、繰り返し読みたい

takewelltakewell

https://nextjs.org/docs/basic-features/built-in-css-support

CSS について

グローバルにスタイルをおきたい場合は、以下のファイルに記述(cssmoduel としてインポート)
pages/_app.js.

Sass は普通にパケージを入れて .scss にすればサポートされる

CSSModule は ビルトインでサポートされている [name].module.css をファイル名にすれば css module として、クラスメイは hash がつく(ユニーク性が担保される) カスケーディングは同じファイル間であれば聞くと思われる (たぶん)

他に CSS in JS 系のライブラリの使い方はドキュメントにある Styled JSX は vercel 製なので推してくるが、使ってるところを見たことがないので CSS in JS を使う場合は

postcss は設定ファイルを弄る必要がありそう、アドバンスド機能でまた掘る

所感

採用したい CSS の記法次第で何にでも対応できそう。この辺僕はあんまり詳しくないけど、nextjs 的には css module 推奨っぽいので postcss でよさそう

takewelltakewell

https://nextjs.org/docs/basic-features/image-optimization

画像最適化の機能
WebP に対応してるブラウザからのリクエストなら webp にして返す、デバイスごとに適切なサイズの画像を返す (sp に巨大画像を送らない)などが可能になる機能がある

API としては イメージタグの拡張として提供される

import Image from 'next/image'

function Home() {
  return (
    <>
      <h1>My Homepage</h1>
      <Image
        src="/me.png"
        alt="Picture of the author"
        width={500}
        height={500}
      />
      <p>Welcome to my homepage!</p>
    </>
  )
}

export default Home

画像が別ドメインから配信されてる場合は

next.config.js に以下を指定すれば OK

module.exports = {
  images: {
    domains: ['example.com'],
  },
}

画像を CDN においている場合は以下

module.exports = {
  images: {
    loader: 'imgix',
    path: 'https://example.com/myaccount/',
  },
}

imgx, cloudinary, akamai あたりはサポートしてるっぽい

これは CDN の挙動に応じて色々抽象化してくれるのだろうか...
あとビルドの時に width height で渡した値にリサイズしてくれるのだろうか? たぶんするのでは...

takewelltakewell

https://nextjs.org/docs/routing/introduction

ルーティングについて

pages/blog/index.js → /blog
pages/blog/first-post.js → /blog/first-post
pages/dashboard/settings/username.js → /dashboard/settings/username

pages/blog/[slug].js → /blog/:slug (/blog/hello-world)
pages/[username]/settings.js → /:username/settings (/foo/settings)
pages/post/[...all].js → /post/* (/post/2020/id/title)

takewelltakewell

https://nextjs.org/docs/advanced-features/custom-document

_app.js が nextjs のグローバルエントリーポイント、_document.js は マークアップのグローバルエントリーポイント?という理解であってるか??

以下がデフォルト getInitialProps というやつがこのクラスコンポーネントには生えていてる


import Document, { Html, Head, Main, NextScript } from 'next/document'

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx)
    return { ...initialProps }
  }

  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default MyDocument