Next.jsでhtml内にinlineのscriptを差し込んでみた
ちょっと所要でNext.jsが後追いで読み込むjsファイルではなく、最初にレンダリングされるhtmlのresponseにインラインのjsが入っており、そこで処理されてほしいなと思ったのでちょっと試してみました。
結論
NextのScriptで書く。書くときには strategy="beforeInteractive" を書く
const script = ` const checkVersion = async () => { const versionOnHtml = "${process.env.NEXT_PUBLIC_BUILD_ID}" const versionFromApi = await fetch("/api/version").then((res) => res.json()) if (versionOnHtml !== versionFromApi.version) { console.error("version mismatch") if(window.confirm("Version mismatch. Do you want to reload the page?")) { window.location.reload() } } } checkVersion() `; const Page = () => { return ( <> <Script id="version-check" strategy="beforeInteractive"> {script} </Script> <h1>Script</h1> </> ); };
Script (next/script)
Script といういかにも、というものがあったのでそれを利用します。
もともとシンプルに以下のように書いて試してみました。
<Script id="my-script">{`console.log('Hello world!');`}</Script>
そうすると返ってきたhtmlの中には存在せず、loadされてるjsファイルに ...script.js なるものが増えていました。
<!DOCTYPE html> <html> <head> <style data-next-hide-fouc="true"> body { display: none } </style> <noscript data-next-hide-fouc="true"> <style> body { display: block } </style> </noscript> <meta charSet="utf-8"/> <meta name="viewport" content="width=device-width"/> <meta name="next-head-count" content="2"/> <noscript data-n-css=""></noscript> <script defer="" nomodule="" src="/_next/static/chunks/polyfills.js?ts=1723445452836"></script> <script src="/_next/static/chunks/webpack.js?ts=1723445452836" defer=""></script> <script src="/_next/static/chunks/main.js?ts=1723445452836" defer=""></script> <script src="/_next/static/chunks/pages/_app.js?ts=1723445452836" defer=""></script> <!-- これ↓ --> <script src="/_next/static/chunks/pages/script.js?ts=1723445452836" defer=""></script> <!-- これ↑ --> <script src="/_next/static/development/_buildManifest.js?ts=1723445452836" defer=""></script> <script src="/_next/static/development/_ssgManifest.js?ts=1723445452836" defer=""></script> <noscript id="__next_css__DO_NOT_USE__"></noscript> </head> <body> <div id="__next"> <h1>Script</h1> </div> <script src="/_next/static/chunks/react-refresh.js?ts=1723445452836"></script> <script id="__NEXT_DATA__" type="application/json"> { "props": { "pageProps": { } }, "page": "/script", "query": { }, "buildId": "development", "nextExport": true, "autoExport": true, "isFallback": false, "scriptLoader": [ ] }</script> </body> </html>
※jsのファイルの中身が莫大だったので割愛
ではどうやってhtml内に書かれる形にしたらいいかを考えたらちゃんと記載がありました。
beforeInteractive Scripts that load with the beforeInteractive strategy are injected into the initial HTML from the server, downloaded before any Next.js module, and executed in the order they are placed before any hydration occurs on the page. Scripts denoted with this strategy are preloaded and fetched before any first-party code, but their execution does not block page hydration from occurring.
なので結果として strategy="beforeInteractive" というのをつける必要があります。
<!DOCTYPE html> <html> <head> <style data-next-hide-fouc="true"> body { display: none } </style> <noscript data-next-hide-fouc="true"> <style> body { display: block } </style> </noscript> <meta charSet="utf-8"/> <meta name="viewport" content="width=device-width"/> <meta name="next-head-count" content="2"/> <!-- ここ↓ --> <script id="version-check" data-nscript="beforeInteractive"> const checkVersion = async()=>{ const versionOnHtml = "test-02271500+a" const versionFromApi = await fetch("/api/version").then((res)=>res.json()) console.log("versionOnHtml", versionOnHtml) console.log("versionFromApi", versionFromApi) if (versionOnHtml !== versionFromApi.version) { console.error("version mismatch") alert("version mismatch") if (window.confirm("Version mismatch. Do you want to reload the page?")) { window.location.reload() } } } checkVersion() </script> <!-- ここ↑ --> <noscript data-n-css=""></noscript> <script defer="" nomodule="" src="/_next/static/chunks/polyfills.js?ts=1723445452836"></script> <script src="/_next/static/chunks/webpack.js?ts=1723445452836" defer=""></script> <script src="/_next/static/chunks/main.js?ts=1723445452836" defer=""></script> <script src="/_next/static/chunks/pages/_app.js?ts=1723445452836" defer=""></script> <script src="/_next/static/chunks/pages/script.js?ts=1723445452836" defer=""></script> <script src="/_next/static/development/_buildManifest.js?ts=1723445452836" defer=""></script> <script src="/_next/static/development/_ssgManifest.js?ts=1723445452836" defer=""></script> <noscript id="__next_css__DO_NOT_USE__"></noscript> </head> <body> <div id="__next"> <h1>Script</h1> </div> <script src="/_next/static/chunks/react-refresh.js?ts=1723445452836"></script> <script id="__NEXT_DATA__" type="application/json"> { "props": { "pageProps": { } }, "page": "/script", "query": { }, "buildId": "development", "nextExport": true, "autoExport": true, "isFallback": false, "scriptLoader": [ ] }</script> </body> </html>
あんまり積極的に使うものではないかもしれませんが、必要に応じて利用していきたいと思います