GatsbyでSPAをつくろうとすると
Gatsby.jsは「爆速サイト生成」として有名な静的サイトジェネレータで、ブログなどの作成管理などで注目されています。
また、その性能からSPAの作成にも有効だと書かれている情報サイト多くあります。
しかし、実際にSPAを作ろうとすると、SPA特有の設定などが必要になります。日本語での制作例や設定例などはまだ少なく、まとめサイトのようなものも見つけにくい状況です。
ここでは、実際に作ってみて分かった、最低限必要な設定などをまとめました。
試行環境
ソフトウェア | バージョン |
---|---|
MasOS | 10.10.5 |
Node.js | 10.16.1 |
React | 16.12.0 |
Gatsby | 2.19.7 |
準備
まずはプロジェクトを作成します。
$ npx gatsby new spa_study
$ cd spa_study
$ npm start
localサーバーが起動するので、ブラウザからlocalhost:8000
にアクセスして開発中の画面を確認します。
ルーティング用のページを作る
src/pages
に新しいページusers.js
を作成します。
このusers.js
に、ユーザー情報をURLに応じて表示するアプリを作っていきます。
index.js
の任意の場所にusers.js
へのリンクを追加します
<Link to="/users/">Go to user list</Link>
@reach-router
gatsby
には@reach-router
がすでに組み込まれています。ルーティング関連はこのライブラリを使用していきます。
user.js
を以下のようにします。
import React from "react"
import { Router, Link } from '@reach/router'
import Layout from "../components/layout"
import SEO from "../components/seo"
const UsersPage = () => (
<Layout>
<SEO title="Users" />
<Router basepath="users">
<UserList path="/" />
<User path=":userId" />
</Router>
</Layout>
)
export default UsersPage
const UserList = () => (
<div>
<h2>ユーザ一覧</h2>
<li><Link to="hoge">Hoge</Link></li>
<li><Link to="hage">Hage</Link></li>
</div>
)
const User = props => (
<div>
<h2>User:{props.userId}</h2>
<Link to="../">Back</Link>
</div>
)
<Link />
コンポーネントをgatsby
から呼び出すか@reach-router
から呼び出すかで挙動がかわるので注意が必要です。
さて、ユーザ一覧のページと各ユーザへのリンクができましたが、この状態でリンク先へいくと、以下のようなエラーが出ます。
170 | if (document.title) {
171 | pageName = document.title
172 | }
> 173 | const pageHeadings = document
174 | .getElementById(`gatsby-focus-wrapper`)
175 | .getElementsByTagName(`h1`)
176 | if (pageHeadings && pageHeadings.length) {
gatsby
では、pagesフォルダに作られたファイルを元に静的ファイルが作成され、サイトを構成します。そのため、存在しないファイルにアクセスしようとするとエラーが出ます。
通常は404エラーとなりますが、<Router />
がクライアントサイドで振り分けているためにjavascriptエラーとなるようです。
gatsby-node.js
上記のjavascriptエラーを回避するために、gatsby
の設定を変更する必要があります。
gatsby-node.js
で使われるonCreatePage
でpagesにはないページを処理します。
exports.onCreatePage = async ({ page, actions }) => {
const { createPage } = actions
if (page.path.match(/^\/users/)) {
page.matchPath = "/users/*"
createPage(page)
}
}
以上を書き加えたのち、localサーバーを再起動すると、ユーザーページへのアクセスが可能になります。
コード中に二回出てくる/users/
が、クライアントサイドで生成されるURLの基点となります。
この設定はプラグインのgatsby-plugin-create-client-paths
を使うことでより簡単になります。
Serverのルーティング設定
SPAでルーティングを制御する場合、URLで指定された場所にはファイルが存在していません。
お気に入りや、手打ちなどでダイレクトにアクセスしようとすると404エラーに遭遇します。
それに対応するためには、サーバー側で、すべてのアクセスをindex.html
などに集約する必要があります。
しかし、gatsby
で作ったSPAは、create-react-app
などで作成した従来のReactSPAと異なり、静的ページが複数生産されています。
そのため、すべてのアクセスをトップページへ誘導する方法ではルーティングの不具合を解消できない場合があります。
今回作ったアプリの場合、<Router />
が記述されているページがusers.js
なので、/users/
以下のアクセスを/users/
へ誘導する必要があります。
ここでは、apacheの設定を紹介します。
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^users/. users/index.html [L]
3/26追記
個別のディレクトリにルールを適用しなくても、クライアントサイドオンリーを表示することができました。
.htaccessに以下の設定を追記することで表示されました。
ErrorDocument 404 /404.html
gatsby
でつくられる404.htmlに一旦転送されたあと、ウェブアプリ内のルーティング機能でレンダリングされました。
開発環境と本番環境でURLの階層が異なる場合
開発用のlocalサーバと本番環境ではURLの構成が異なる場合があります。
- 開発://myserver.com/index.html
- 本番://rentalserver.com/myapp/index.html
この場合、ルートからの階層が異なるため、@reach-router
でのルーティング条件が適合しなくなります。
その場合は、basepath
の引数をprops
から取ることで回避できます。
const UsersPage = (props) => (
<Layout>
<SEO title="Users" />
<Router basepath={props.uri}>
<Home path="/" />
<User path=":userId" />
</Router>
</Layout>
)
まとめ
gatsby
でのSPAルーティングは同梱されている@reach-router
を使用する。
動的ページへのアクセスにはgatsby-node.js
などでルーティングの設定が必要になる。
動的ページの元となっているページごとにサーバ側のルーティング処理を設定する必要がある。
gatsby
でSPAを作成すると、pagesのフォルダ構成がサイトマップになるので、全体像を把握しやすく、機能をどんどん追加しても管理がしやすいのがメリットだと思います。
反面、URLを元にする動的ページが増えると、ルーティング処理の管理ができなくなっていきそうです。
爆速が魅力のgatsby SPAですが、create-react-app
の方が設定項目も少なくて、SPAの入門にはよさそうです。