【Next.js】画像・webフォント・スクリプトの最適化

Next.jsでは、アセット類を最適化できるコンポーネントやロジックが提供されており、パフォーマンスの良さに定評があります。

## 画像の最適化
Imageコンポーネントを用いることで、下記のような最適化を行ってくれます。

* 最適な拡張子の画像が生成によるパフォーマンス向上
* レイアウトシフトの防止
* 遅延読み込みによる、ページの読み込み速度の最適化


### 使い方
`next/image`からimportして使います。

```jsx
import Image from 'next/image'

export const ImageContainer = () => (
  <Image 
    // props
  />
)
```

Imageコンポーネントで使えるpropsの型は、`ImageProps`でexportされております。

```jsx
export declare type ImageProps = Omit<JSX.IntrinsicElements['img'], 'src' | 'srcSet' | 'ref' | 'width' | 'height' | 'loading' | 'style'> & {
    src: string | StaticImport;                         // 画像のソース(ex: /images/test.png)
    width?: number | string;                            // 画像の横幅(layout='fill'以外は必須)
    height?: number | string;                           // 画像の高さ(layout='fill'以外は必須)
    layout?: LayoutValue;                               // viewport を変更した時のレイアウト
    loader?: ImageLoader;                               // 画像のURLを生成する関数
    quality?: number | string;                          // 画質(0〜100, デフォ75)
    priority?: boolean;                                 // preloadするか (trueでページ遷移時にpreload)
    loading?: LoadingValue;                             // 遅延ロードする ("lazy" | "eager")
    lazyBoundary?: string;                              // 遅延読み込みをトリガーする閾値の設定(デフォ 200px)
    placeholder?: PlaceholderValue;                     // プレースホルダーの形式("blur" | "empty", デフォ empty)
    blurDataURL?: string;                               // placeholder="blur"の時のブラーする画像のURL
    unoptimized?: boolean;                              // 最適化しないか (デフォ false - 最適化される)
    objectFit?: ImgElementStyle['objectFit'];           // layout='fill'の時のobject-fit
    objectPosition?: ImgElementStyle['objectPosition']; // layout='fill'の時のobject-position
    onLoadingComplete?: OnLoadingComplete;              // 画像を完全ロード直後に実行されるコールバック関数
}; 
```

ものすごくオプションがたくさんありますが、基本的には`src` `layout` `width` `height` を使います。

### layoutモードに関して

`layout`には、`fill` `fixed` `intrinsic` `responsive` の四種類あります。

#### fill
親の要素の`width` / `height`に合わせて画像の幅と高さが設定される。

#### fixed
指定した`width` / `height` に常に固定される。


#### responsive
指定した`width` / `height`の比率は保ちながら、ビューポートに合わせてサイズが変わる。

#### intrinsic
`responsive`と同じく、指定した`width` / `height`の比率は保ちながら、ビューポートに合わせてサイズが変わるが、指定した`width`は超えられない。

![参考.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142322/86672e5b-7584-0136-a60d-8cd630814ed1.gif)

(筆者本人が神戸でとった写真だよ！天気が悪かったなぁ :umbrella: )

## フォントの最適化
Next.js v10.2 以降から、webフォントが最適化されるようになりました。

最適化される内容としては、下記になります。

* フォントCSSを自動的にインライン化
* 表示の高速化 


下記記事をみていただくとわかる通り、はんっぱなく[LCP](https://web.dev/lcp/)が改善されます。
すごい。

https://zenn.dev/catnose99/articles/bb943c3dc99d89


### 使い方

#### 1. `next/head`を使う
Headコンポーネント内でwebフォントを読み込むと最適化されます。

```jsx
import Head from 'next/head'

export default page = () => {
  return (
    <div>
      <Head>
        <link
          href="https://fonts.googleapis.com/css2?family=Inter&display=optional"
          rel="stylesheet"
        />
      </Head>
    </div>
  )
}
```
#### 2. `next/document`を使う

`next/document`のHeadコンポーネントでwebフォントを読み込むと最適化されます。


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

class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head>
          <link
            href="https://fonts.googleapis.com/css2?family=Inter&display=optional"
            rel="stylesheet"
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default MyDocument
```


## スクリプトの最適化
Scriptコンポーネントを使うことで、スクリプトの読み込みを最適化することができます。
主に、計測・分析タグなどのサードパーティスクリプトの読み込みを最適化するために使います。

### 使い方
`next/script`からimportして使います。

```jsx
import Script from 'next/script'

export const ImageContainer = () => (
  <Script src="https://www.google-analytics.com/analytics.js" />
)
```

Scriptコンポーネントで使えるpropsの型は、`ScriptProps`でexportされております。

```jsx
export interface ScriptProps extends ScriptHTMLAttributes<HTMLScriptElement> {
  strategy?: 'afterInteractive' | 'lazyOnload' | 'beforeInteractive'; // 読み込みタイミング (デフォはafterInteractive)
  id?: string;                                                        //　読み込みを順を最適するための追跡id
  onLoad?: (e: any) => void;                                          // ロード後に実行する関数
  onError?: (e: any) => void;                                         // エラー時に実行する関数
  children?: React.ReactNode;
}
```


### strategyに関して
`strategy`には、`afterInteractive` `lazyOnload` `beforeInteractive` の3種類あります。
順序は、`beforeInteractive` → `afterInteractive` → `lazyOnload` です。
詳しいタイミングを説明します。

### beforeInteractive
ページがインタラクティブになる前に実行されます。
（つまり、自分の書いたjavascriptよりも先に実行されることになります。)

下記のような重要なスクリプトに使われます。

* ボット検出
* cookieなどの同意管理

#### afterInteractive (デフォ値)
ページがインタラクティブになった後に実行されます。

下記のような重要なスクリプトに使われます。

* タグマネージャー
* 分析系のタグ

#### lazyOnload 
すべてのリソースがフェッチされた後、アイドル時間中にロードされます。



このコードを本気で読解した記事を発見し、とても興味津々に読んでいたのですが、
よくみたら自分が大尊敬している会社の先輩の記事でした:innocent:

https://zenn.dev/aiji42/articles/9a6ab12ab5f6e6