12
8

More than 3 years have passed since last update.

【Gatsbyで作るSPA】 Client-only Routes を設定する

Last updated at Posted at 2020-03-13

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へのリンクを追加します

index.js
    <Link to="/users/">Go to user list</Link>

@reach-router

gatsbyには@reach-routerがすでに組み込まれています。ルーティング関連はこのライブラリを使用していきます。
user.jsを以下のようにします。

users.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にはないページを処理します。

gatsby-node.js
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の設定を紹介します。

.htaccess
  RewriteEngine on
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule ^users/. users/index.html [L]

3/26追記

個別のディレクトリにルールを適用しなくても、クライアントサイドオンリーを表示することができました。
.htaccessに以下の設定を追記することで表示されました。

.htaccess
  ErrorDocument 404 /404.html

gatsbyでつくられる404.htmlに一旦転送されたあと、ウェブアプリ内のルーティング機能でレンダリングされました。

開発環境と本番環境でURLの階層が異なる場合

開発用のlocalサーバと本番環境ではURLの構成が異なる場合があります。

  • 開発://myserver.com/index.html
  • 本番://rentalserver.com/myapp/index.html

この場合、ルートからの階層が異なるため、@reach-routerでのルーティング条件が適合しなくなります。
その場合は、basepathの引数をpropsから取ることで回避できます。

users.js
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の入門にはよさそうです。

参考

12
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
8