App Router のRoutingについて考察
はじめに
だいぶ App Router に慣れてきたので、便利なところと、おや?と思うところを整理したいと思います。今回は Routing
についてのご紹介・考察です。
基本的な Routing のおさらい
/src
/app
/dashboard
/page.tsx
/settings
/page.tsx
/api
/route.ts
というような構成にすることで、
/dashboard
/dashboard/settings
/api
という具合に非常に簡単かつ直感的な Routing を実現できます。
Routing の機構
公式を見てみると、他にも機構があるようなので、ご紹介します。今回は以下の 3 つを取り上げて、それぞれについて、使い勝手の良さとか感想をお伝えします。あくまで個人の見解です。
1. Route Groups
/src
/app
/(user)
/dashboard
/page.tsx
/profile
/page.tsx
/layout.tsx <- /dashboard, /profile に適用されるレイアウト
/(admin)
/setting
/page.tsx
/users
/page.tsx
/layout.tsx <- /setting, /users に適用されるレイアウト
個人的に衝撃を受けた機構ですが、(xxx)
という命名規則でフォルダを作成することで その配下のフォルダをくくることができます。
例えば、上記のように user 用と admin 用で画面のレイアウトを分けたい場合、または画面によって、ナビゲーションの表示有無を分けたい場合などが挙げられます。
画面レイアウトの統制の取りやすさが大きな利点だと考えます。
2. Module Path Aliases
// before
import { Button } from "../../../../components/ui";
// after
import { Button } from "@/ui";
使用したいコンポーネントを import する際に、絶対パスや相対パスで指定することがあると思いますが、page.tsx
が Routing を担っている関係で、とても深い階層からの呼び出しがされることが起こり得ます。そういった場合に、有効なのが、この機構です。
Module Path Aliases にあるように、とても簡単に設定できますが、
create-next-app
コマンドを使って、プロジェクトを新規作成する場合は、
What is your project named? my-app
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like to use `src/` directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to customize the default import alias (@/*)? No / Yes
このように対話的に設定が可能になっています。
import 文は他の言語でもそうですが、読みづらくなる傾向があるように思えます。開発端末の解像度がよくないと PR では見切れたり、折り返しになったりすることもあるので、そこを端的に表現できるようになるのは、レビュー観点で嬉しい利点だと考えます。
3. Private Folders
/src
/app
/dashboard
/page.tsx
/_components <- この配下はRoutingされない
/index.ts
/DashboardContainer.tsx
/DashboardPresenter.tsx
というように、フォルダ名の接頭辞に _
を付与したフォルダにすることで、/_components
配下を Routing から除外することができます。
これにより意図しない Routing がされないのですが、private folder
に配置するものには注意が必要かと思いました。
まずはじめに考えたいのが、ボタンやタブといった他のページからも共有して参照する UI コンポーネントです。これは、ほぼ自明ですが、これらを private に配置すると、他が参照すると構成が歪になるので、避けるべきだと思います。
もう一つは、そのページ固有のコンポーネントです。上記は、ページを構成するコンポーネントを配置した例です。中身は省略しますが、責務分割すると
ページの Routing が責務
import { Dashboard } from './_components';
export default Dashboard;
ページで扱うデータの加工が責務
export { DashboardContainer as Dashboard } from './_components/DashboardContainer';
import { DashboardPresenter } from './_components/DashboardPresenter';
export const DashboardContainer = () => {
...
return <DashboardPresenter {...} />
}
ページの UI 決定が責務
export const DashboardPresenter = () => <div>Dashboardのページです</div>
というようになります。責務については、以前まとめたものがあるので、よければご覧ください。
ページ固有なものなので、全く問題ないでしょう。公式にもあるように、標準的な使い方であり、弊社のプロジェクトでも採用している構成ルールです。
なんですが、私の得意とする分野がリファクタリングとか仕様変更容易性といった領域なので、その観点で見たときに、最近 違和感を覚え始めました。というのも、page.tsx
は App router
から導入された仕様であり、また新たな Routing 方法が導入されたときに、_components
配下のフォルダ移動を余儀なくされます。「たかが、フォルダ移動なので、それくらいやれば?」という意見は多いと思いますが、しなくていい改修はしない方がいいかと今は思っています。ページ数が巨大になれば、こういうリファクタリングコストが顕著に現れてきます。
まだ検討段階ですが、
/src
/app
/dashboard
/page.tsx
/components
/page
/Dashboard
/index.ts
/DashboardContainer.tsx
/DashboardPresenter.tsx
/ui
/Button.tsx
/Tabs.tsx
という構成にすることで、 page.tsx
による Routing が変更になったとしても、 /components/page/
配下は無用なリファクタリングをすることなく継続的に使用することができます。
まとめ
さまざまな機構があり、便利なものもありますが、private folder
って使う場面ないんじゃないかなあと思っています。あくまで個人の見解です。
フロントエンドの開発は、瞬く間にバージョンアップされる傾向があり、NextJs 14
へのバージョンアップも近々起こりそうな予感がしています(2023/10/24 現在)。フロントエンドの中枢となる部分が、フレームワークに依存しない構成であれば、バージョンアップの気軽さ、だけでなく開発の生産性も向上するのではないかと考えます。
フィシルコムのテックブログです。マーケティングSaaSを開発しています。 マイクロサービス・AWS・NextJS・Golang・GraphQLに関する発信が多めです。 カジュアル面談はこちら(ficilcom.notion.site/bbceed45c3e8471691ee4076250cd4b1)から
Discussion