サードパーティのスクリプトを外部スクリプトとして読み込みたいが、面倒臭いことに検証用と本番用でスクリプトがわかれているため、ドメインをみて動的に読み込むようにしたい
また、面倒なのはSSRなら簡単だが、静的ファイルの中でこれをやりたいというのと、以下のようにそのスクリプトを読んだ上で実行しなければならないスクリプトがある
<script src="https://dev.third-party-script/sample.js" /> ... <script> // ThirdPartyは上のscriptを読み込んで利用できる ThirdParty.execute("args"); </script>
調べたところrequestIdleCallbackを使えばできることがわかったが、実装が複雑になってしまうため導入をためらっていた
そんなとき、Next.jsを利用しようと考えていたのだが、 ちょうどNext11で導入されたnext/script
がこれを解決してくれた
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
をつかってやりたいことが実現できた