UGA Boxxx

つぶやきの延長のつもりで、知ったこと思ったこと書いてます

【Next.js】next/scriptで外部スクリプト読み込みの最適化

サードパーティスクリプトを外部スクリプトとして読み込みたいが、面倒臭いことに検証用と本番用でスクリプトがわかれているため、ドメインをみて動的に読み込むようにしたい

また、面倒なのはSSRなら簡単だが、静的ファイルの中でこれをやりたいというのと、以下のようにそのスクリプトを読んだ上で実行しなければならないスクリプトがある

<script src="https://dev.third-party-script/sample.js" />
...
<script>
  // ThirdPartyは上のscriptを読み込んで利用できる
  ThirdParty.execute("args");
</script>

調べたところrequestIdleCallbackを使えばできることがわかったが、実装が複雑になってしまうため導入をためらっていた

そんなとき、Next.jsを利用しようと考えていたのだが、 ちょうどNext11で導入されたnext/scriptがこれを解決してくれた

nextjs.org

next/script

next/scriptサードパーティスクリプトの読み込み優先度を設定して、読み込みパフォーマンスを向上させることで、特に何もしなければ以下のように記述することでasyncをつけた読み込みを自動でしてくれる

// pages/index.js
import Script from 'next/script'

export default function Home() {
  return (
    <>
      <Script src="https://www.google-analytics.com/analytics.js" />
    </>
  )
}

他にもstrategyプロパティを使用することで、スクリプトの読み込みに優先順位を付けることができる

strategyプロパティに指定できるのは以下

  • beforeInteractive
    生成されるHTMLに最初から挿入されており、バンドルされた JavaScript が実行される前に実行される
    ページがインタラクティブになる前にフェッチして実行したい場合に利用する

  • afterInteractive(デフォルト)
    クライアント側に挿入され、ハイドレーション後に実行される ページがインタラクティブになった後に、フェッチして実行したい場合に利用する

  • lazyOnload
    アイドルになるまでロードを遅延でしたい場合に利用する

スクリプトの直列実行

外部スクリプトのロード後にコードを実行したい場合もサポートされており、これによりやりたいことが実現できた

具体的には以下のようにonLoadを使う

<Script
  src={url} // consent mangagement
  strategy="beforeInteractive"
  onLoad={() => {
    // If loaded successfully, then you can load other scripts in sequence
  }}
/>

SSGでの利用

今回の場合、Next.jsをサーバーとしてはホストはせず、SSGで生成した静的ファイルだけをホストしたい

SSGした静的ファイルでもnext/script機能は使えるのだが、SSGは事前にSSRが実行されて生成される仕組みなので、windowオブジェクトの利用している箇所は注意が必要だった

例えば、ドメインをみて判断したいURLはwindow.location.hostnameを使う想定だったが、SSR時にはwindowは定義されていないのでSSR時には実行しないようtypeof window === ''undefined"で回避する必要があった

他にもonLoadの関数の中で、次のよう書いてしまうと

  onLoad={() => {
    ThirdParty.execute("args");
  }}

SSR時にThirdPartyが未定義でエラーになってしまうので、これも回避する必要がある

  onLoad={() => {
    if (typeof window === 'undefined') return;
    ThirdParty.execute("args");
  }}

このような感じで、next/scriptをつかってやりたいことが実現できた

他参考

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