jQuery製のページをGatsbyの静的ページに移行時、develop環境を正常に動作させる
極力やりたくはないが、どうしても jQuery 製の Web ページを Gatsby の静的ページに載せ替えたいケースに使える方法。
jQuery 製ページを Gatsby 移行した時に発生する問題
例えばWordPressで作成されたホームページを Gatsby に移行したいとする。また、このホームページには全ページで共通で読み込まれるcommon.jsというスクリプトファイルがあり、jQuery で作られているとする。
この時、どのページも大体以下のような構成になっているはずである。
<head>〜</head>
<body>
<script src="~/jquery.min.js">
<script src="~/common.js">
</body>
このcommon.jsを static なファイルとしてそのまま利用したい場合、gatsby のhtml.jsは以下のようになる。
import React from "react"
import PropTypes from "prop-types"
export default function HTML(props) {
return (
<html>
<head>〜</head>
<body>
〜
<div
key={`body`}
id="___gatsby"
dangerouslySetInnerHTML={{ __html: props.body }}
/>
<script src="~/jquery.min.js">
<script src="~/common.js">
</body>
</html>
)
}
これは、リリースビルド(gatsby build
の生成結果)のソースではうまく動作する。
しかし、gatsby develop
で develop 環境を立ち上げた場合にうまく動作しない。
gatsby developがうまくいかない原因
gatsby develop起動時、開発中の Web ページは React アプリケーションとして動作する。この react レンダリングのためのスクリプトは一番最後に読み込まれる。 一方、common.jsは jQuery ベースであり、body要素内の DOM が全て用意できているという前提の作りになっている。よって、
jquery.min.js
スクリプト実行- common.jsスクリプト実行
- gatsby の開発環境用スクリプトが実行され、react によって body 要素内の DOM がレンダリングされる
という順序になり、DOM が用意されている前提のcommon.jsの実行が失敗してしまう。
develop 環境を動作させる方法
develop 環境でも正常に動作させるには、react のレンダリングが終わった後にcommon.jsが読み込まれるようにすればよい。
1. 環境変数の定義
これを実現するためには develop 環境のみの分岐処理が必要なので、環境変数の設定ファイル.env.development
を作成し、変数を定義する。
GATSBY_ENV=development
GATSBY_ プレフィックスがついている変数のみがブラウザレベルで利用できるため、GATSBY_をつけている。
2. common.jsの読み込みを html.js から行わないように変更
develop 環境では、html.js内でcommon.jsの読み込みを行わないようにする。
import React from "react"
import PropTypes from "prop-types"
export default function HTML(props) {
return (
<html>
<head>〜</head>
<body>
〜
<div
key={`body`}
id="___gatsby"
dangerouslySetInnerHTML={{ __html: props.body }}
/>
<script src="~/jquery.min.js">
{process.env.GATSBY_ENV === "development" ? (
<></>
) : (
<script type="text/javascript" src="/js/common.js"></script>
)}
</body>
</html>
)
}
3. react のレンダリング完了後にcommon.jsを読み込むように変更
適当にLayoutコンポーネントを用意し、全てのページでLayoutコンポーネントが読み込まれるようにしておく。
const Page = ({ anything }) => {
return <Layout>...</Layout>
}
Layoutコンポーネント内では、DOM のレンダリングが終わった後にcommon.jsを無理矢理読み込む。
const Layout = ({ children }) => {
const [loadJs, setLoadJs] = useState(false)
useEffect(() => {
if (process.env.GATSBY_ENV === "development") {
if (!loadJs) {
const head = document.getElementsByTagName("head")[0]
const scriptUrl = document.createElement("script")
scriptUrl.type = "text/javascript"
scriptUrl.src = "/js/common.js"
head.appendChild(scriptUrl)
setLoadJs(true)
}
}
}, [])
return <div>...</div>
}