Jaka jest standardowa struktura katalogów projektu Next.js?
Standardowa struktura katalogów projektu Next.js nie jest jednym poprawnym drzewem folderów, które identycznie pasuje do każdego zespołu. Oznacza raczej stosowanie określonych konwencji dla plików routingu, które framework musi interpretować, przy jednoczesnym rozmieszczaniu reszty kodu zgodnie z charakterem projektu. W nowym projekcie łatwym do zrozumienia podejściem jest zwykle uczynienie src/app centrum adresów URL i punktów wejścia stron, wydzielenie UI używanego w wielu miejscach do components, umieszczanie logiki specyficznej dla funkcji w features, gdy jest potrzebna, oraz przechowywanie współdzielonych narzędzi w lib. App Router jest głównym podejściem do routingu w aktualnym Next.js, a Pages Router nadal jest obsługiwany. nextjs.orgnextjs.org
Przy pierwszym definiowaniu struktury ważne nie jest tworzenie jak największej liczby folderów. Najpierw rozróżnij, które pliki tworzą adresy URL, które obsługują layouty, ekrany błędów i API oraz który kod jest używany wyłącznie na konkretnym ekranie. Ten artykuł omawia łatwą w utrzymaniu strukturę skoncentrowaną na App Routerze, opartą na tych rozróżnieniach.
Dlaczego w Next.js nie ma „jednej standardowej struktury”?
Next.js udostępnia konwencje budowania tras z plików i folderów, ale nie narzuca dokładnie, które foldery muszą zawierać każdy komponent i każdą część logiki biznesowej. Szczególnie w App Routerze foldery reprezentują segmenty URL, a pliki specjalne, takie jak page.tsx i route.ts, tworzą rzeczywiste publiczne punkty wejścia. Natomiast zwykłe pliki umieszczone wewnątrz trasy domyślnie nie stają się zewnętrznymi trasami. nextjs.org
Ze względu na tę cechę struktury mogą różnić się nawet między aplikacjami Next.js. Mała strona marketingowa może potrzebować jedynie app i kilku współdzielonych komponentów. Z kolei w usłudze obejmującej wiele domen, takich jak panele, zamówienia i konta, oddzielenie kodu specyficznego dla funkcji od kodu współdzielonego ułatwia zrozumienie zakresu zmian. Nie jest to bezwzględna reguła wymagana przez Next.js, lecz podejście do organizacji kodu wybierane przez zespół ponad konwencjami routingu.
Dlatego mniej mylące jest myślenie o „standardzie” w następujących dwóch warstwach.
| Kategoria | Charakter | Typowe przykłady |
|---|---|---|
| Konwencje interpretowane przez Next.js | Nazwy i położenie plików bezpośrednio wpływają na zachowanie | app/page.tsx, app/layout.tsx, app/api/users/route.ts |
| Konwencje zdefiniowane przez projekt | Zespół definiuje nazwy i granice zgodnie ze swoimi potrzebami | components, features, lib, hooks, types |
Dowolna zmiana pierwszej warstwy może zmienić routing lub zachowanie specjalnego UI. Druga warstwa może być pomijana albo łączona zależnie od skali i złożoności domeny. Na przykład struktura bez features jest możliwa i w małym projekcie nie ma potrzeby nadmiernie dzielić każdego fragmentu kodu.
Dlaczego nowe projekty należy projektować wokół App Routera?
App Router to podejście do budowania stron i layoutów oparte na katalogu app. Oficjalna dokumentacja zaleca przejście z Pages Routera do App Routera, aby korzystać z najnowszych funkcji Reacta, przy czym sam Pages Router nadal jest obsługiwany. Dlatego podczas utrzymywania lub poznawania istniejącej struktury projektu warto rozumieć konwencje pages, ale przy projektowaniu nowej struktury naturalne jest traktowanie App Routera jako domyślnego kandydata. nextjs.org
Kluczowe w App Routerze jest połączenie hierarchii URL z hierarchią plików. Na przykład app/dashboard/page.tsx staje się punktem wejścia strony /dashboard, a znajdujący się poniżej app/dashboard/layout.tsx może zapewnić layout stosowany do podtras panelu. Główny app/layout.tsx jest root layoutem otaczającym wszystkie trasy. nextjs.org
Takie podejście dobrze nadaje się do przechowywania kodu na poziomie ekranu blisko siebie. Karty, tabele i UI filtrów potrzebne wyłącznie w /dashboard mogą znajdować się w app/dashboard, a przyciski lub pola wejściowe używane ponownie na wielu ekranach można przenieść do zewnętrznego folderu współdzielonego. Kluczowym kryterium nie jest „jakiej technologii użyto do napisania tego pliku”, ale „w jakim zakresie jest on ponownie używany?”.
Korzystanie z App Routera nie oznacza jednak, że cały kod musi znaleźć się w app. Możesz traktować app jako granicę, która ułatwia odczyt adresów URL i plików specjalnych, a współdzielony kod lub złożone implementacje funkcji rozdzielać do innych folderów w miarę potrzeb. Next.js nie wykonuje tego podziału automatycznie; jest to konwencja strukturalna, którą zespół definiuje i konsekwentnie stosuje.
Kiedy używać folderu src i co pozostaje w katalogu głównym?
src jest opcjonalny. Gdy go używasz, możesz zgromadzić app i kod źródłowy aplikacji w src, wizualnie oddzielając pliki konfiguracyjne od kodu wykonywanego w czasie działania. Natomiast public, package.json, next.config.js, tsconfig.json i pliki .env.* należą do katalogu głównego projektu. nextjs.org
Poniżej znajduje się jeden łatwy do zrozumienia przykład użycia App Routera wraz z src.
my-app/
├─ public/
│ ├─ images/
│ └─ fonts/
├─ src/
│ ├─ app/
│ │ ├─ layout.tsx
│ │ ├─ page.tsx
│ │ ├─ globals.css
│ │ ├─ (marketing)/
│ │ │ └─ about/
│ │ │ └─ page.tsx
│ │ ├─ dashboard/
│ │ │ ├─ layout.tsx
│ │ │ ├─ page.tsx
│ │ │ ├─ loading.tsx
│ │ │ ├─ error.tsx
│ │ │ └─ _components/
│ │ └─ api/
│ │ └─ users/
│ │ └─ route.ts
│ ├─ components/
│ ├─ features/
│ ├─ lib/
│ ├─ hooks/
│ └─ types/
├─ .env.local
├─ next.config.js
├─ package.json
└─ tsconfig.json
W tym przykładzie src jest tylko granicą dla kodu aplikacji, a nie wymaganym mechanizmem zmieniającym zachowanie. Jeśli istniejący projekt ma już app w katalogu głównym, konsekwentne stosowanie ustalonej konwencji może być lepsze niż wymuszanie dodania src. W szczególności jeśli katalogi o nazwach app lub pages istnieją zarówno w katalogu głównym, jak i w src, pierwszeństwo ma katalog główny, dlatego podczas migracji ważne jest, aby przez dłuższy czas nie pozostawiać zduplikowanych struktur. nextjs.org
Praktyczne kryterium wyboru src jest proste. Jest przydatny, jeśli chcesz wyraźnie oddzielić pliki konfiguracyjne od kodu produktu albo spodziewasz się wzrostu liczby plików źródłowych. Z drugiej strony nie ma potrzeby stosować go w projekcie edukacyjnym czy bardzo małym projekcie, którego struktura katalogu głównego jest już przejrzysta.
Które pliki w folderze app tworzą rzeczywiste trasy?
W App Routerze foldery reprezentują części adresu URL, czyli segmenty. Jednak utworzenie samego folderu app/dashboard nie czyni /dashboard publiczną stroną. Jeśli folder zawiera page.tsx, staje się trasą udostępniającą UI strony; jeśli zawiera route.ts, staje się punktem końcowym API opartym na Route Handlerze. nextjs.org
Na przykład rozważ następujący układ.
src/app/
├─ page.tsx
├─ about/
│ └─ page.tsx
├─ dashboard/
│ ├─ page.tsx
│ └─ reports/
│ └─ page.tsx
└─ api/
└─ users/
└─ route.ts
W tym przypadku page.tsx odpowiada kolejno za /, /about, /dashboard i /dashboard/reports. api/users/route.ts nie jest stroną UI; definiuje punkt końcowy API. Gdy zrozumiesz, że page.tsx i route.ts pełnią funkcję publicznych punktów wejścia trasy, staje się jasne, dlaczego inne pliki mogą znajdować się w tym samym folderze. nextjs.org
Tę właściwość można postrzegać jako kolokację. Kolokacja to podejście organizacyjne polegające na trzymaniu powiązanego kodu blisko siebie. Na przykład komponenty tabel używane tylko w dashboard, funkcje formatowania specyficzne dla ekranu oraz kod transformujący dane testowe mogą być umieszczone blisko app/dashboard. Nie oznacza to, że folderów współdzielonych nigdy nie należy używać. Wystarczy przenieść kod używany ponownie przez inne trasy do folderów o szerszym zakresie.
Jak rozdzielać pliki layout, loading i error?
layout.tsx obsługuje współdzieloną powłokę UI. Główny app/layout.tsx otacza wszystkie trasy, natomiast layout.tsx w folderze podrzędnym jest stosowany zagnieżdżenie do jego podtras. Na przykład, jeśli nawigacja panelu jest współdzielona przez /dashboard i /dashboard/reports, może znajdować się w app/dashboard/layout.tsx. nextjs.org
page.tsx to UI strony wyświetlane pod określoną ścieżką. Jeśli layout jest powtarzalną strukturą zewnętrzną, strona jest bliższa zawartości specyficznej dla ścieżki, która zmienia się wewnątrz niej. Ich rozdzielenie oznacza, że nie trzeba powielać współdzielonej nawigacji ani ramek na każdej stronie.
App Router udostępnia również zarezerwowane pliki dla UI specyficznego dla stanu. loading.tsx służy do UI ładowania, error.tsx do UI błędów, a not-found.tsx do UI braku wyniku. W przeciwieństwie do zwykłych plików komponentów, Next.js interpretuje te pliki według konkretnych ról, dlatego należy umieszczać je z uwzględnieniem ich roli i zakresu. nextjs.org
Na przykład, jeśli chcesz wyświetlić osobny ekran podczas przygotowywania danych w /dashboard, możesz rozważyć app/dashboard/loading.tsx; jeśli potrzebujesz ekranu obsługi błędów w tym zakresie, możesz rozważyć app/dashboard/error.tsx. Nie ma potrzeby mechanicznego dodawania tych plików do każdego folderu. Podejmowanie decyzji na podstawie tego, czy dana trasa rzeczywiście potrzebuje osobnego UI stanu oczekiwania, błędu lub braku wyniku, zachowuje zwięzłość struktury.
Jak dynamiczne trasy i trasy API są reprezentowane w nazwach folderów?
Użyj notacji nawiasów kwadratowych, gdy część trasy nie jest z góry określona. [slug] oznacza jeden dynamiczny segment, [...slug] oznacza wszystkie kolejne podsegmenty, a [[...slug]] oznacza opcjonalną formę, w której te podsegmenty mogą nie występować. nextjs.org
Na przykład stronę, której identyfikator wpisu się zmienia, można zorganizować następująco.
src/app/posts/
└─ [slug]/
└─ page.tsx
W tej strukturze [slug] nie jest stałą nazwą folderu; to symbol zastępczy, który przyjmuje zmieniającą się część URL. Natomiast gdy wiele poziomów trasy musi być obsługiwanych przez jedną konwencję, rozważ [...slug] lub [[...slug]]. Wybór notacji zależy od tego, czy musi istnieć co najmniej jedna podścieżka oraz czy przypadek bez ścieżki również powinien być obsłużony przez ten sam ekran. nextjs.org
Podczas budowania API route.ts jest punktem wejścia Route Handlera. Dlatego możesz reprezentować strukturę URL za pomocą folderów takich jak app/api/users/route.ts i umieszczać route.ts na końcu. Zarówno dla stron, jak i API, odczyt hierarchii folderów pozwala wywnioskować przybliżoną ścieżkę. Warto jednak odpowiednio rozdzielać dedykowane pliki implementacyjne, aby kod UI i kod przetwarzania po stronie serwera nie stały się nadmiernie wymieszane i rozbudowane w tym samym obszarze. nextjs.org
Dlaczego potrzebne są grupy tras i foldery prywatne?
Folder ujęty w nawiasy, taki jak (marketing), jest grupą tras. To logiczne grupowanie, które nie jest uwzględniane w URL. Możesz na przykład grupować strony marketingowe, takie jak informacje o firmie i cennik, zarządzając jednocześnie ekranami korzystania z produktu w osobnej strukturze. app/(marketing)/about/page.tsx staje się trasą /about bez nazwy grupy. nextjs.org
Grupy tras są przydatne, gdy chcesz pokazać granice layoutów lub własności kodu bez zmieniania URL. Ponieważ jednak nazwa grupy nie pojawia się w URL, konflikt wystąpi, jeśli odrębne grupy ostatecznie utworzą ten sam URL. Ponadto konfiguracja przechodząca między wieloma root layoutami może powodować pełne załadowanie strony, więc podziału root layoutów nie należy stosować pochopnie tylko po to, aby foldery wyglądały schludnie. nextjs.org
Foldery zaczynające się od podkreślenia, takie jak _components i _lib, są folderami prywatnymi i są wyłączone z routingu. W App Routerze zwykłe pliki domyślnie nie stają się trasami, więc podkreślenie nie jest bezwzględnie konieczne. Może jednak być przydatne, gdy chcesz widocznie oznaczyć granicę między specjalnymi plikami routingu a wewnętrzną implementacją lub uniknąć pomyłki z zarezerwowanymi nazwami plików. nextjs.org
Na przykład app/dashboard/_components/summary-card.tsx komunikuje, że jest to UI specyficzne dla panelu. Jeśli jednak komponent zacznie być wielokrotnie używany w innych funkcjach, bardziej naturalne będzie rozważenie przeniesienia go z folderu z podkreśleniem do współdzielonego components lub odpowiedniej granicy funkcji. Pamiętaj, że prefiks folderu nie jest mechanizmem kontroli dostępu; to notacja komunikująca rolę kodu.
Jak rozróżniać components, features, lib, hooks i types?
Te foldery są opcjonalnymi wyborami organizacyjnymi, a nie zarezerwowanymi konwencjami App Routera. Dlatego uzgodnione przez zespół granice odpowiedzialności mają większe znaczenie niż same nazwy. Poniższe rozróżnienia są częstym punktem wyjścia.
| Folder | Kod zwykle w nim umieszczany | Kryterium umieszczenia |
|---|---|---|
components | UI używane ponownie na wielu ekranach | Czy nie jest powiązane z konkretnym URL ani domeną? |
features | Implementacja na poziomie funkcji lub domeny | Czy istnieje wyraźne pojęcie biznesowe, takie jak konta, zamówienia lub panele? |
lib | Współdzielone narzędzia i klienci | Czy jest to współdzielone narzędzie, a nie UI? |
hooks | Wielokrotnego użytku hooki | Czy wiele komponentów współdzieli ten sam stan lub zachowanie? |
types | Współdzielone typy | Czy wiele obszarów odwołuje się do tych samych definicji typów? |
components może zawierać nie tylko ogólne przyciski, ale też złożone UI współdzielone przez wiele funkcji. Jednak tworzenie od początku z każdego elementu UI globalnie współdzielonego komponentu może abstrahować implementacje, które w rzeczywistości są potrzebne tylko na jednym ekranie. Innym podejściem jest początkowe trzymanie go blisko trasy, a następnie przeniesienie, gdy jest stabilnie używany w co najmniej dwóch miejscach i ma jasno określony współdzielony interfejs.
features jest szczególnie czytelne, gdy potrzebna jest struktura skoncentrowana na domenach. Na przykład, jeśli zamówienia i konta mają niezależne ekrany, UI i kod przetwarzania danych, możesz grupować je jako features/orders i features/account. Natomiast tworzenie zbyt wielu folderów funkcji na prostej stronie może zmuszać ludzi do przechodzenia między wieloma folderami tylko po to, aby znaleźć pliki. Lepiej wprowadzać je wyłącznie wtedy, gdy granice funkcji odpowiadają rzeczywistym pojęciom produktu.
lib jest kandydatem na lokalizację dla niebędącego UI kodu bazowego, takiego jak współdzielone narzędzia czy klienci serwerowi. Jeśli jednak każdy plik z funkcjami będzie gromadzony w lib, może on stać się dużym, trudnym do zrozumienia magazynem. Praktyczna reguła to trzymanie narzędzi używanych przez tylko jedną funkcję blisko tej funkcji lub trasy i przenoszenie do lib wyłącznie kodu współdzielonego w wielu miejscach.
Dlaczego public i pliki zmiennych środowiskowych należą do katalogu głównego projektu?
public to folder w katalogu głównym projektu przeznaczony na pliki statyczne. Umieszczone tam pliki są serwowane z głównej ścieżki; na przykład do public/profile.png odwołujesz się jako /profile.png. Zapewnia to jedno miejsce na pliki serwowane statycznie, takie jak obrazy i fonty. nextjs.org
Nawet przy użyciu src nie należy postrzegać struktury jako przeniesienia public do src/public. public pozostaje w katalogu głównym projektu, a package.json, next.config.js, tsconfig.json i .env.* również są zarządzane z katalogu głównego. W szczególności, ponieważ lokalne pliki zmiennych środowiskowych, takie jak .env.local, mogą zawierać sekrety, ważne jest ustanowienie zasady operacyjnej, aby nie dodawać ich do kontroli wersji. nextjs.org
Rozróżnienie ról zasobów statycznych i kodu źródłowego aplikacji ułatwia interpretację ścieżek. Pliki w src/app są kodem budującym ekrany lub routing, natomiast pliki w public są zasobami statycznymi, do których odwołujesz się przez URL. Nawet gdy ten sam obraz jest używany na ekranie, trzeba inaczej rozumieć jego sposób dostarczania i ścieżkę odwołania, zależnie od miejsca umieszczenia pliku.
Czym różni się to od struktury Pages Routera?
Pages Router działa, traktując pliki w katalogu pages jako trasy. Na przykład pages/index.tsx odpowiada /, a pages/about.tsx odpowiada /about. Konwencje zarezerwowanych plików przypisują także specjalne role _app, _document, 404, 500 i innym. nextjs.org
Łatwo popełnić błędy, jeśli postrzega się App Router i Pages Router jako podejścia różniące się tylko nazwą folderu. W App Routerze pliki specjalne, takie jak page.tsx, layout.tsx i route.ts, znajdujące się pod segmentami folderów, dzielą odpowiedzialności, podczas gdy zwykłe pliki domyślnie nie są trasami. W Pages Routerze pliki wewnątrz pages są bardziej bezpośrednio powiązane z trasami. nextjs.orgnextjs.org
Podczas pracy z istniejącym projektem Pages Routera należy respektować jego aktualne konwencje oparte na pages. Natomiast przy rozpoczynaniu nowego projektu oparcie struktury na przykładzie skoncentrowanym na App Routerze i dodawanie folderów organizacyjnych tylko wtedy, gdy są rzeczywiście potrzebne, zmniejsza narzut. Ważne jest, aby nie mieszać konwencji obu routerów w ramach tego samego projektu struktury katalogów.
Jakich kryteriów użyć przy wyborze struktury dla rzeczywistego projektu?
Najpierw zacznij od struktury URL. Wypisz kluczowe ścieżki, do których będą przechodzić użytkownicy, a następnie określ, z których współdzielonych layoutów korzysta każda ścieżka. Przedstaw wynik za pomocą folderów w app oraz rozmieszczenia page.tsx i layout.tsx. Używaj grup tras, gdy istnieje wyraźny powód do podziału obszarów ekranów lub layoutów bez zmiany URL. nextjs.orgnextjs.org
Po drugie, oceń zakres ponownego użycia. Trzymaj kod używany tylko przez jedną trasę blisko tej trasy. UI używane przez wiele tras może zostać przeniesione do szerszego zakresu, takiego jak components, a narzędzia używane przez wiele funkcji mogą trafić do lib. Zamiast uogólniać wszystko od początku, rozdzielanie kodu w miarę pojawiania się rzeczywistych wzorców ponownego użycia i zmian pomaga ograniczyć niepotrzebną abstrakcję.
Po trzecie, uwzględnij niezależność funkcji. Jeśli funkcje o wyraźnych obszarach odpowiedzialności i terminologii — takie jak konta, administracja lub zamówienia — stają się większe, granice domen, takie jak features, mogą być przydatne. Z drugiej strony, gdy ekranów jest niewiele, a różnice między funkcjami są słabe, wystarczyć może kolokacja w app wraz z niewielką liczbą folderów współdzielonych.
Po czwarte, sprawdź koszt odnajdywania kodu przez zespół. Gdy nowy członek zespołu szuka kodu dla konkretnego URL, powinien móc podążyć ścieżką app i znaleźć stronę oraz dedykowaną implementację. Nazwy i lokalizacje współdzielonych przycisków lub narzędzi również powinny być przewidywalne. Dobra struktura wynika bardziej z tej przewidywalności niż z modnych nazw folderów.
Jakich błędnych przekonań i pułapek strukturalnych należy unikać?
Pierwsze błędne przekonanie brzmi: „utworzenie folderu od razu tworzy URL”. W App Routerze folder reprezentuje segment, ale potrzebuje page.tsx lub route.ts, aby stać się publiczną stroną albo punktem końcowym API. Ta reguła pozwala przechowywać powiązane pliki wewnętrzne razem w folderze trasy. nextjs.org
Drugie błędne przekonanie brzmi: „bez folderu z podkreśleniem wszystkie pliki wewnętrzne są publicznie dostępne”. Zwykłe pliki w App Routerze domyślnie nie są trasami. Nazwa taka jak _components nie jest wymaganym zabezpieczeniem; to narzędzie organizacyjne, które wskazuje wewnętrzną implementację i wyłącza folder z routingu. nextjs.org
Trzecie błędne przekonanie brzmi: „nazwy grup tras również są zawarte w URL”. Nazwa w nawiasach w (marketing) jest wyłączona z URL. Z powodu tej wygody należy upewnić się, że różne grupy nie tworzą tego samego końcowego URL. Jeśli dzielisz wiele root layoutów, możliwość pełnego załadowania strony podczas nawigacji między grupami to kolejny element do rozważenia przed zaprojektowaniem struktury. nextjs.org
Na koniec unikaj błędu polegającego na pozostawieniu odpowiadających sobie katalogów app lub pages zarówno w katalogu głównym, jak i w src podczas wprowadzania src. Ponieważ w tym przypadku katalog główny ma pierwszeństwo, może wyglądać na to, że oczekiwane źródło nie jest wykonywane. Zmiana struktury nie jest po prostu zadaniem jednorazowego dodania folderów; postępuj, weryfikując, który katalog faktycznie stanowi podstawę routingu. nextjs.org
Podsumowanie: standard dotyczy granic ról, a nie listy folderów
Punktem wyjścia dla struktury projektu Next.js są konwencje routingu definiowane przez app i pliki specjalne. W nowym projekcie App Routera możesz uczynić src/app centrum adresów URL, stron i layoutów; zachować public jako folder zasobów statycznych w katalogu głównym; oraz wybierać components, features, lib, hooks i types zgodnie z rzeczywistym zakresem ponownego użycia kodu i złożonością domeny. nextjs.orgnextjs.org
Ostatecznie dobra struktura nie jest tą z największą liczbą folderów. Jest to struktura, w której członkowie zespołu mogą łatwo przewidzieć położenie ekranu dla konkretnej trasy, kodu dedykowanego temu ekranowi oraz kodu współdzielonego w wielu miejscach. Dokładnie przestrzegaj konwencji plików Next.js, a następnie stopniowo dostosowuj organizację ponad nimi w miarę ewolucji tempa wzrostu projektu i wzorców zmian.