『サーバーサイドWebアプリ』と『SPA』のルーティングの違い

そもそもSPAにおけるルーティングとは?

通常のMVCで作られたサーバーサイドWebアプリケーションで、ルーティングとは何かと問われたら、リクエストされたURLに対して、そのURLに紐づくページをアプリケーションサーバーが、クライアントに返す事 という答えになるかと思いますが、SPAでのルーティングは、解釈が異なります。

SPAは、サーバーへの初回リクエストに対して、そのURLに関わらず、アプリ全体が記述されたJSのコードとアセットファイルがごそっと返されます。以降、アプリ内部でのページ遷移は、アプリが動的にDOMを書き換えることにより、移動してるように見せてるだけである点が、大きな特徴です。これは、ブラウザのアドレスバーのURLが書き換わっても、その処理もブラウザの中で完結しており、実際にサーバーへリクエストを飛ばすことは原則的に無いという事です。

では、サーバーにリクエストを送る事なく、ルーティングを実現するために、どうしてるか?これは、History APIという技術が使われています。HTML5から導入された__pushState()__ と__replaceState()__ で、ブラウザのセッション履歴に任意のURLを追加したり、特定の履歴を書き換えたりしながら、JSで履歴のURLを制御することを可能にしています。

詳しくは、MDNのドキュメントへ。(時間のある時に一読しておくことをお奨めします)

以上の事から、SPAのルーティングとは?という問いに対しては、以下の解釈が妥当になるかと思います。

SPAにおけるルーティングとは、『DOMの動的な書き換えによってページ遷移を擬似的に実現するとともに、ブラウザのセッション履歴をそれに同期させる事』

...と言えます。

SPAでのルーティングの適用単位は、コンポーネントです。LaravelやRailsだと、ひとつのURLに対応するのはControllerで、それがviewを経由して、ページ全体を描画するが、Reactは、ルートのコンポーネントから階層を下り、ここから先はこのコンポーネントがマウントされる、このパスの時には別のコンポーネントがマウントされるといったように、コンポーネントそのものがルーティングとなります。

その他、開発時に考慮しなければいけない前提として、以下が挙げられます。

サーバーへリクエストを投げないということは...

① サーバー側から、クライアントがどんなページを見てるか、どう移動したかは分からない。これはアクセス解析時にネックとなる。(これは、ルーティングが実行されてURLが書き換わる際に、Effect HookでGAにリクエストを発行する処理を挟むことで対処できる)

② HTTPステータスコードを返せない。

③ pushStateを使ったルーティングでは、 履歴が変わって、新しいページがレンダリングされても、スクロール位置は遷移前のままとなる。(サーバーサイドであれば、ページ遷移すれば、毎回トップ位置から表示される)この場合、以下のようなコードを書いて対処する必要がある。

const {hash, pathname} = useLocation();
const {action} = useHistory();

useEffect(()=> {
    if (!hash || action !== 'POP') {
      window.scrollTop(0,0);
    } 
  }, [action, hash, pathname])