Απελευθερώστε τη δύναμη του Next.js App Router με τον αναλυτικό μας οδηγό για τη δρομολόγηση βάσει αρχείων. Μάθετε πώς να δομείτε την εφαρμογή σας, να δημιουργείτε δυναμικές διαδρομές, να διαχειρίζεστε διατάξεις και πολλά άλλα.
App Router του Next.js: Ένας Ολοκληρωμένος Οδηγός για τη Δρομολόγηση Βάσει Αρχείων
Ο App Router του Next.js, που εισήχθη στο Next.js 13 και καθιερώθηκε στις νεότερες εκδόσεις, φέρνει επανάσταση στον τρόπο με τον οποίο δομούμε και πλοηγούμαστε στις εφαρμογές. Εισάγει ένα ισχυρό και διαισθητικό σύστημα δρομολόγησης βάσει αρχείων που απλοποιεί την ανάπτυξη, βελτιώνει την απόδοση και ενισχύει τη συνολική εμπειρία του προγραμματιστή. Αυτός ο ολοκληρωμένος οδηγός θα εμβαθύνει στη δρομολόγηση βάσει αρχείων του App Router, παρέχοντάς σας τις γνώσεις και τις δεξιότητες για να δημιουργήσετε στιβαρές και επεκτάσιμες εφαρμογές Next.js.
Τι είναι το File-Based Routing;
Το file-based routing (δρομολόγηση βάσει αρχείων) είναι ένα σύστημα δρομολόγησης όπου η δομή των διαδρομών της εφαρμογής σας καθορίζεται άμεσα από την οργάνωση των αρχείων και των καταλόγων σας. Στον App Router του Next.js, ορίζετε τις διαδρομές δημιουργώντας αρχεία μέσα στον κατάλογο `app`. Κάθε φάκελος αντιπροσωπεύει ένα τμήμα διαδρομής (route segment) και ειδικά αρχεία μέσα σε αυτούς τους φακέλους καθορίζουν πώς θα χειριστεί αυτό το τμήμα διαδρομής. Αυτή η προσέγγιση προσφέρει πολλά πλεονεκτήματα:
- Διαισθητική Δομή: Το σύστημα αρχείων αντικατοπτρίζει τη δομή των διαδρομών της εφαρμογής, καθιστώντας την εύκολη στην κατανόηση και την πλοήγηση.
- Αυτόματη Δρομολόγηση: Το Next.js δημιουργεί αυτόματα διαδρομές με βάση τη δομή των αρχείων σας, εξαλείφοντας την ανάγκη για μη αυτόματη ρύθμιση.
- Συντοπισμός Κώδικα (Code Collocation): Οι διαχειριστές διαδρομών (route handlers) και τα στοιχεία UI (UI components) βρίσκονται μαζί, βελτιώνοντας την οργάνωση και τη συντηρησιμότητα του κώδικα.
- Ενσωματωμένες Δυνατότητες: Ο App Router παρέχει ενσωματωμένη υποστήριξη για διατάξεις (layouts), δυναμικές διαδρομές, ανάκτηση δεδομένων (data fetching) και πολλά άλλα, απλοποιώντας πολύπλοκα σενάρια δρομολόγησης.
Ξεκινώντας με τον App Router
Για να χρησιμοποιήσετε τον App Router, πρέπει να δημιουργήσετε ένα νέο project Next.js ή να μεταφέρετε ένα υπάρχον. Βεβαιωθείτε ότι χρησιμοποιείτε την έκδοση 13 του Next.js ή νεότερη.
Δημιουργία Νέου Project:
Μπορείτε να δημιουργήσετε ένα νέο project Next.js με τον App Router χρησιμοποιώντας την παρακάτω εντολή:
npx create-next-app@latest my-app --example with-app
Μεταφορά Υπάρχοντος Project:
Για να μεταφέρετε ένα υπάρχον project, πρέπει να μετακινήσετε τις σελίδες σας από τον κατάλογο `pages` στον κατάλογο `app`. Ίσως χρειαστεί να προσαρμόσετε τη λογική δρομολόγησής σας ανάλογα. Το Next.js παρέχει έναν οδηγό μετεγκατάστασης για να σας βοηθήσει με αυτή τη διαδικασία.
Βασικές Έννοιες της Δρομολόγησης Βάσει Αρχείων
Ο App Router εισάγει διάφορα ειδικά αρχεία και συμβάσεις που καθορίζουν τον τρόπο χειρισμού των διαδρομών σας:
1. Ο Κατάλογος `app`
Ο κατάλογος `app` είναι η ρίζα των διαδρομών της εφαρμογής σας. Όλα τα αρχεία και οι φάκελοι μέσα σε αυτόν τον κατάλογο θα χρησιμοποιηθούν για τη δημιουργία διαδρομών. Οτιδήποτε βρίσκεται εκτός του καταλόγου `app` (όπως ο κατάλογος `pages` αν κάνετε μετεγκατάσταση) θα αγνοηθεί από τον App Router.
2. Το Αρχείο `page.js`
Το αρχείο `page.js` (ή `page.jsx`, `page.ts`, `page.tsx`) είναι το πιο θεμελιώδες μέρος του App Router. Ορίζει το στοιχείο UI που θα αποδοθεί για ένα συγκεκριμένο τμήμα διαδρομής. Είναι ένα **απαιτούμενο** αρχείο για κάθε τμήμα διαδρομής που θέλετε να είναι άμεσα προσβάσιμο.
Παράδειγμα:
Αν έχετε μια δομή αρχείων σαν αυτή:
app/
about/
page.js
Το component που εξάγεται από το `app/about/page.js` θα αποδοθεί όταν ένας χρήστης πλοηγηθεί στη διεύθυνση `/about`.
// app/about/page.js
import React from 'react';
export default function AboutPage() {
return (
<div>
<h1>Σχετικά με Εμάς</h1>
<p>Μάθετε περισσότερα για την εταιρεία μας.</p>
</div>
);
}
3. Το Αρχείο `layout.js`
Το αρχείο `layout.js` (ή `layout.jsx`, `layout.ts`, `layout.tsx`) ορίζει ένα UI που είναι κοινόχρηστο σε πολλές σελίδες εντός ενός τμήματος διαδρομής. Οι διατάξεις (layouts) είναι χρήσιμες για τη δημιουργία σταθερών κεφαλίδων, υποσέλιδων, πλευρικών στηλών και άλλων στοιχείων που πρέπει να υπάρχουν σε πολλές σελίδες.
Παράδειγμα:
Ας πούμε ότι θέλετε να προσθέσετε μια κεφαλίδα τόσο στη σελίδα `/about` όσο και σε μια υποθετική σελίδα `/about/team`. Μπορείτε να δημιουργήσετε ένα αρχείο `layout.js` στον κατάλογο `app/about`:
// app/about/layout.js
import React from 'react';
export default function AboutLayout({ children }) {
return (
<div>
<header>
<h1>Σχετικά με την Εταιρεία μας</h1>
</header>
<main>{children}</main>
</div>
);
}
Το prop `children` θα αντικατασταθεί με το UI που αποδίδεται από το αρχείο `page.js` στον ίδιο κατάλογο ή σε οποιουσδήποτε ένθετους καταλόγους.
4. Το Αρχείο `template.js`
Το αρχείο `template.js` είναι παρόμοιο με το `layout.js`, αλλά δημιουργεί μια νέα παρουσία (instance) του component για κάθε παιδική διαδρομή. Αυτό είναι χρήσιμο για σενάρια όπου θέλετε να διατηρήσετε την κατάσταση (state) του component ή να αποτρέψετε την εκ νέου απόδοση (re-renders) κατά την πλοήγηση μεταξύ παιδικών διαδρομών. Σε αντίθεση με τις διατάξεις, τα πρότυπα (templates) θα αποδίδονται εκ νέου κατά την πλοήγηση. Η χρήση προτύπων είναι εξαιρετική για την εμψύχωση στοιχείων κατά την πλοήγηση.
Παράδειγμα:
// app/template.js
'use client'
import { useState } from 'react'
export default function Template({ children }) {
const [count, setCount] = useState(0)
return (
<main>
<p>Πρότυπο: {count}</p>
<button onClick={() => setCount(count + 1)}>Ενημέρωση Προτύπου</button>
{children}
</main>
)
}
5. Το Αρχείο `loading.js`
Το αρχείο `loading.js` (ή `loading.jsx`, `loading.ts`, `loading.tsx`) σας επιτρέπει να δημιουργήσετε ένα UI φόρτωσης που εμφανίζεται ενώ φορτώνεται ένα τμήμα διαδρομής. Αυτό είναι χρήσιμο για την παροχή καλύτερης εμπειρίας χρήστη κατά την ανάκτηση δεδομένων ή την εκτέλεση άλλων ασύγχρονων λειτουργιών.
Παράδειγμα:
// app/about/loading.js
import React from 'react';
export default function Loading() {
return <p>Φόρτωση πληροφοριών "σχετικά με"...</p>;
}
Όταν ένας χρήστης πλοηγηθεί στο `/about`, το component `Loading` θα εμφανιστεί μέχρι να αποδοθεί πλήρως το component του `page.js`.
6. Το Αρχείο `error.js`
Το αρχείο `error.js` (ή `error.jsx`, `error.ts`, `error.tsx`) σας επιτρέπει να δημιουργήσετε ένα προσαρμοσμένο UI σφάλματος που εμφανίζεται όταν προκύπτει σφάλμα σε ένα τμήμα διαδρομής. Αυτό είναι χρήσιμο για την παροχή ενός πιο φιλικού προς το χρήστη μηνύματος σφάλματος και την αποφυγή της κατάρρευσης ολόκληρης της εφαρμογής.
Παράδειγμα:
// app/about/error.js
'use client'
import React from 'react';
export default function Error({ error, reset }) {
return (
<div>
<h2>Παρουσιάστηκε σφάλμα!</h2>
<p>{error.message}</p>
<button onClick={() => reset()}>Δοκιμάστε ξανά</button>
</div>
);
}
Εάν προκύψει σφάλμα κατά την απόδοση της σελίδας `/about`, θα εμφανιστεί το component `Error`. Το prop `error` περιέχει πληροφορίες σχετικά με το σφάλμα, και η συνάρτηση `reset` επιτρέπει στον χρήστη να προσπαθήσει να φορτώσει ξανά τη σελίδα.
7. Ομάδες Διαδρομών (Route Groups)
Οι Ομάδες Διαδρομών `(groupName)` σας επιτρέπουν να οργανώνετε τις διαδρομές σας χωρίς να επηρεάζεται η δομή του URL. Δημιουργούνται περικλείοντας το όνομα ενός φακέλου σε παρενθέσεις. Αυτό είναι ιδιαίτερα χρήσιμο για την οργάνωση διατάξεων και κοινόχρηστων components.
Παράδειγμα:
app/
(marketing)/
about/
page.js
contact/
page.js
(shop)/
products/
page.js
Σε αυτό το παράδειγμα, οι σελίδες `about` και `contact` ομαδοποιούνται κάτω από την ομάδα `marketing`, και η σελίδα `products` κάτω από την ομάδα `shop`. Τα URLs παραμένουν `/about`, `/contact`, και `/products`, αντίστοιχα.
8. Δυναμικές Διαδρομές (Dynamic Routes)
Οι δυναμικές διαδρομές σας επιτρέπουν να δημιουργείτε διαδρομές με μεταβλητά τμήματα. Αυτό είναι χρήσιμο για την εμφάνιση περιεχομένου που βασίζεται σε δεδομένα που ανακτώνται από μια βάση δεδομένων ή ένα API. Τα δυναμικά τμήματα διαδρομών ορίζονται περικλείοντας το όνομα του τμήματος σε αγκύλες (π.χ., `[id]`).
Παράδειγμα:
Ας πούμε ότι θέλετε να δημιουργήσετε μια διαδρομή για την εμφάνιση μεμονωμένων αναρτήσεων ιστολογίου με βάση το ID τους. Μπορείτε να δημιουργήσετε μια δομή αρχείων σαν αυτή:
app/
blog/
[id]/
page.js
Το τμήμα `[id]` είναι ένα δυναμικό τμήμα. Το component που εξάγεται από το `app/blog/[id]/page.js` θα αποδοθεί όταν ένας χρήστης πλοηγηθεί σε ένα URL όπως `/blog/123` ή `/blog/456`. Η τιμή της παραμέτρου `id` θα είναι διαθέσιμη στο prop `params` του component.
// app/blog/[id]/page.js
import React from 'react';
export default async function BlogPost({ params }) {
const { id } = params;
// Ανάκτηση δεδομένων για την ανάρτηση ιστολογίου με το δεδομένο ID
const post = await fetchBlogPost(id);
if (!post) {
return <p>Η ανάρτηση ιστολογίου δεν βρέθηκε.</p>;
}
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
);
}
async function fetchBlogPost(id) {
// Προσομοίωση ανάκτησης δεδομένων από μια βάση δεδομένων ή API
return new Promise((resolve) => {
setTimeout(() => {
const posts = {
'123': { title: 'Η Πρώτη μου Ανάρτηση στο Ιστολόγιο', content: 'Αυτό είναι το περιεχόμενο της πρώτης μου ανάρτησης στο ιστολόγιο.' },
'456': { title: 'Μια Άλλη Ανάρτηση στο Ιστολόγιο', content: 'Αυτό είναι λίγο πιο συναρπαστικό περιεχόμενο.' },
};
resolve(posts[id] || null);
}, 500);
});
}
Μπορείτε επίσης να χρησιμοποιήσετε πολλαπλά δυναμικά τμήματα σε μια διαδρομή. Για παράδειγμα, θα μπορούσατε να έχετε μια διαδρομή όπως `/blog/[category]/[id]`.
9. Τμήματα Catch-all (Catch-all Segments)
Τα τμήματα Catch-all σας επιτρέπουν να δημιουργείτε διαδρομές που ταιριάζουν με οποιονδήποτε αριθμό τμημάτων. Αυτό είναι χρήσιμο για σενάρια όπως η δημιουργία ενός CMS όπου η δομή του URL καθορίζεται από τον χρήστη. Τα τμήματα Catch-all ορίζονται προσθέτοντας τρεις τελείες πριν από το όνομα του τμήματος (π.χ., `[...slug]`).
Παράδειγμα:
app/
docs/
[...slug]/
page.js
Το τμήμα `[...slug]` θα ταιριάξει με οποιονδήποτε αριθμό τμημάτων μετά το `/docs`. Για παράδειγμα, θα ταιριάξει με τα `/docs/getting-started`, `/docs/api/users`, και `/docs/advanced/configuration`. Η τιμή της παραμέτρου `slug` θα είναι ένας πίνακας που περιέχει τα ταιριασμένα τμήματα.
// app/docs/[...slug]/page.js
import React from 'react';
export default function DocsPage({ params }) {
const { slug } = params;
return (
<div>
<h1>Έγγραφα</h1>
<p>Slug: {slug ? slug.join('/') : 'No slug'}</p>
</div>
);
}
Προαιρετικά τμήματα catch-all μπορούν να δημιουργηθούν περικλείοντας το όνομα του τμήματος σε διπλές αγκύλες `[[...slug]]`. Αυτό καθιστά το τμήμα της διαδρομής προαιρετικό. Παράδειγμα:
app/
blog/
[[...slug]]/
page.js
Αυτή η ρύθμιση θα αποδώσει το component page.js τόσο στο `/blog` όσο και στο `/blog/any/number/of/segments`.
10. Παράλληλες Διαδρομές (Parallel Routes)
Οι Παράλληλες Διαδρομές σας επιτρέπουν να αποδίδετε ταυτόχρονα μία ή περισσότερες σελίδες στην ίδια διάταξη. Αυτό είναι ιδιαίτερα χρήσιμο για πολύπλοκες διατάξεις, όπως πίνακες ελέγχου (dashboards), όπου διαφορετικά τμήματα της σελίδας μπορούν να φορτωθούν ανεξάρτητα. Οι Παράλληλες Διαδρομές ορίζονται χρησιμοποιώντας το σύμβολο `@` ακολουθούμενο από ένα όνομα υποδοχής (slot name) (π.χ., `@sidebar`, `@main`).
Παράδειγμα:
app/
@sidebar/
page.js // Περιεχόμενο για την πλευρική στήλη
@main/
page.js // Περιεχόμενο για το κύριο τμήμα
default.js // Απαιτείται: Ορίζει την προεπιλεγμένη διάταξη για τις παράλληλες διαδρομές
Το αρχείο `default.js` απαιτείται όταν χρησιμοποιείτε παράλληλες διαδρομές. Καθορίζει πώς συνδυάζονται οι διάφορες υποδοχές για τη δημιουργία της τελικής διάταξης.
// app/default.js
export default function RootLayout({ children: { sidebar, main } }) {
return (
<div style={{ display: 'flex' }}>
<aside style={{ width: '200px', backgroundColor: '#f0f0f0' }}>
{sidebar}
</aside>
<main style={{ flex: 1, padding: '20px' }}>
{main}
</main>
</div>
);
}
11. Παρεμβαλλόμενες Διαδρομές (Intercepting Routes)
Οι Παρεμβαλλόμενες Διαδρομές σας επιτρέπουν να φορτώσετε μια διαδρομή από ένα διαφορετικό μέρος της εφαρμογής σας μέσα στην τρέχουσα διάταξη. Αυτό είναι χρήσιμο για τη δημιουργία modals, γκαλερί εικόνων και άλλων στοιχείων UI που πρέπει να εμφανίζονται πάνω από το υπάρχον περιεχόμενο της σελίδας. Οι Παρεμβαλλόμενες Διαδρομές ορίζονται χρησιμοποιώντας τη σύνταξη `(..)` , η οποία υποδεικνύει πόσα επίπεδα πάνω στο δέντρο καταλόγων πρέπει να πάτε για να βρείτε την παρεμβαλλόμενη διαδρομή.
Παράδειγμα:
app/
(.)photos/
[id]/
page.js // Η παρεμβαλλόμενη διαδρομή
feed/
page.js // Η σελίδα όπου εμφανίζεται το modal της φωτογραφίας
Σε αυτό το παράδειγμα, όταν ένας χρήστης κάνει κλικ σε μια φωτογραφία στη σελίδα `/feed`, η διαδρομή `app/(.)photos/[id]/page.js` παρεμβάλλεται και εμφανίζεται ως modal πάνω από τη σελίδα `/feed`. Η σύνταξη `(.)` λέει στο Next.js να ψάξει ένα επίπεδο πάνω (στον κατάλογο `app`) για να βρει τη διαδρομή `photos/[id]`.
Ανάκτηση Δεδομένων με τον App Router
Ο App Router παρέχει ενσωματωμένη υποστήριξη για την ανάκτηση δεδομένων χρησιμοποιώντας Server Components και Client Components. Τα Server Components αποδίδονται στον διακομιστή, ενώ τα Client Components αποδίδονται στον πελάτη (client). Αυτό σας επιτρέπει να επιλέξετε την καλύτερη προσέγγιση για κάθε component με βάση τις απαιτήσεις του.
Server Components
Τα Server Components είναι η προεπιλογή στον App Router. Σας επιτρέπουν να ανακτάτε δεδομένα απευθείας στα components σας χωρίς την ανάγκη για ξεχωριστές διαδρομές API. Αυτό μπορεί να βελτιώσει την απόδοση και να απλοποιήσει τον κώδικά σας.
Παράδειγμα:
// app/products/page.js
import React from 'react';
export default async function ProductsPage() {
const products = await fetchProducts();
return (
<div>
<h1>Προϊόντα</h1>
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</div>
);
}
async function fetchProducts() {
// Προσομοίωση ανάκτησης δεδομένων από μια βάση δεδομένων ή API
return new Promise((resolve) => {
setTimeout(() => {
const products = [
{ id: 1, name: 'Προϊόν Α' },
{ id: 2, name: 'Προϊόν Β' },
{ id: 3, name: 'Προϊόν Γ' },
];
resolve(products);
}, 500);
});
}
Σε αυτό το παράδειγμα, η συνάρτηση `fetchProducts` καλείται απευθείας μέσα στο component `ProductsPage`. Το component αποδίδεται στον διακομιστή και τα δεδομένα ανακτώνται πριν αποσταλεί το HTML στον πελάτη.
Client Components
Τα Client Components αποδίδονται στον πελάτη και σας επιτρέπουν να χρησιμοποιείτε λειτουργίες από την πλευρά του πελάτη, όπως event listeners, state και browser APIs. Για να χρησιμοποιήσετε ένα Client Component, πρέπει να προσθέσετε την οδηγία `'use client'` στην κορυφή του αρχείου.
Παράδειγμα:
// app/counter/page.js
'use client'
import React, { useState } from 'react';
export default function CounterPage() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Μετρητής</h1>
<p>Μέτρηση: {count}</p>
<button onClick={() => setCount(count + 1)}>Αύξηση</button>
</div>
);
}
Σε αυτό το παράδειγμα, το component `CounterPage` είναι ένα Client Component επειδή χρησιμοποιεί το hook `useState`. Η οδηγία `'use client'` λέει στο Next.js να αποδώσει αυτό το component στον πελάτη.
Προηγμένες Τεχνικές Δρομολόγησης
Ο App Router προσφέρει αρκετές προηγμένες τεχνικές δρομολόγησης που μπορούν να χρησιμοποιηθούν για τη δημιουργία πολύπλοκων και εξελιγμένων εφαρμογών.
1. Διαχειριστές Διαδρομών (Route Handlers)
Οι Route Handlers σας επιτρέπουν να δημιουργείτε τελικά σημεία API (API endpoints) μέσα στον κατάλογό σας `app`. Αυτό εξαλείφει την ανάγκη για έναν ξεχωριστό κατάλογο `pages/api`. Οι Route Handlers ορίζονται σε αρχεία με το όνομα `route.js` (ή `route.ts`) και εξάγουν συναρτήσεις που χειρίζονται διαφορετικές μεθόδους HTTP (π.χ., `GET`, `POST`, `PUT`, `DELETE`).
Παράδειγμα:
// app/api/users/route.js
import { NextResponse } from 'next/server'
export async function GET(request) {
// Προσομοίωση ανάκτησης χρηστών από μια βάση δεδομένων
const users = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Doe' },
];
return NextResponse.json(users);
}
export async function POST(request) {
const body = await request.json()
console.log('Ληφθέντα δεδομένα:', body)
return NextResponse.json({ message: 'Ο χρήστης δημιουργήθηκε' }, { status: 201 })
}
Αυτό το παράδειγμα ορίζει έναν διαχειριστή διαδρομής στο `/api/users` που χειρίζεται τόσο αιτήματα `GET` όσο και `POST`. Η συνάρτηση `GET` επιστρέφει μια λίστα χρηστών και η συνάρτηση `POST` δημιουργεί έναν νέο χρήστη.
2. Ομάδες Διαδρομών με Πολλαπλές Διατάξεις
Μπορείτε να συνδυάσετε ομάδες διαδρομών με διατάξεις για να δημιουργήσετε διαφορετικές διατάξεις για διαφορετικά τμήματα της εφαρμογής σας. Αυτό είναι χρήσιμο για σενάρια όπου θέλετε να έχετε διαφορετική κεφαλίδα ή πλευρική στήλη για διαφορετικά μέρη του ιστότοπού σας.
Παράδειγμα:
app/
(marketing)/
layout.js // Διάταξη Marketing
about/
page.js
contact/
page.js
(admin)/
layout.js // Διάταξη Admin
dashboard/
page.js
Σε αυτό το παράδειγμα, οι σελίδες `about` και `contact` θα χρησιμοποιούν τη διάταξη `marketing`, ενώ η σελίδα `dashboard` θα χρησιμοποιεί τη διάταξη `admin`.
3. Middleware
Το Middleware σας επιτρέπει να εκτελείτε κώδικα πριν ένα αίτημα χειριστεί από την εφαρμογή σας. Αυτό είναι χρήσιμο για εργασίες όπως ο έλεγχος ταυτότητας, η εξουσιοδότηση, η καταγραφή και η ανακατεύθυνση χρηστών με βάση την τοποθεσία ή τη συσκευή τους.
Το Middleware ορίζεται σε ένα αρχείο με το όνομα `middleware.js` (ή `middleware.ts`) στη ρίζα του project σας.
Παράδειγμα:
// middleware.js
import { NextResponse } from 'next/server'
export function middleware(request) {
// Έλεγχος εάν ο χρήστης είναι πιστοποιημένος
const isAuthenticated = false; // Αντικαταστήστε με τη δική σας λογική ελέγχου ταυτότητας
if (!isAuthenticated && request.nextUrl.pathname.startsWith('/admin')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
// Δείτε το "Matching Paths" παρακάτω για να μάθετε περισσότερα
export const config = {
matcher: '/admin/:path*',
}
Αυτό το παράδειγμα ορίζει ένα middleware που ελέγχει εάν ο χρήστης είναι πιστοποιημένος πριν του επιτρέψει την πρόσβαση σε οποιαδήποτε διαδρομή κάτω από το `/admin`. Εάν ο χρήστης δεν είναι πιστοποιημένος, ανακατευθύνεται στη σελίδα `/login`.
Βέλτιστες Πρακτικές για τη Δρομολόγηση Βάσει Αρχείων
Για να αξιοποιήσετε στο έπακρο το σύστημα δρομολόγησης βάσει αρχείων του App Router, λάβετε υπόψη τις ακόλουθες βέλτιστες πρακτικές:
- Διατηρήστε τη δομή των αρχείων σας οργανωμένη: Χρησιμοποιήστε ουσιαστικά ονόματα φακέλων και ομαδοποιήστε τα σχετικά αρχεία.
- Χρησιμοποιήστε διατάξεις για κοινόχρηστο UI: Δημιουργήστε διατάξεις για κεφαλίδες, υποσέλιδα, πλευρικές στήλες και άλλα στοιχεία που είναι κοινά σε πολλές σελίδες.
- Χρησιμοποιήστε UI φόρτωσης: Παρέχετε UI φόρτωσης για διαδρομές που ανακτούν δεδομένα ή εκτελούν άλλες ασύγχρονες λειτουργίες.
- Χειριστείτε τα σφάλματα με χάρη: Δημιουργήστε προσαρμοσμένα UI σφαλμάτων για να παρέχετε μια καλύτερη εμπειρία χρήστη όταν προκύπτουν σφάλματα.
- Χρησιμοποιήστε ομάδες διαδρομών για οργάνωση: Χρησιμοποιήστε ομάδες διαδρομών για να οργανώσετε τις διαδρομές σας χωρίς να επηρεάζεται η δομή του URL.
- Αξιοποιήστε τα server components για απόδοση: Χρησιμοποιήστε server components για την ανάκτηση δεδομένων και την απόδοση του UI στον διακομιστή, βελτιώνοντας την απόδοση και το SEO.
- Χρησιμοποιήστε client components όταν είναι απαραίτητο: Χρησιμοποιήστε client components όταν χρειάζεστε λειτουργίες από την πλευρά του πελάτη, όπως event listeners, state και browser APIs.
Παραδείγματα Διεθνοποίησης με τον Next.js App Router
Ο Next.js App Router απλοποιεί τη διεθνοποίηση (i18n) μέσω της δρομολόγησης βάσει αρχείων. Δείτε πώς μπορείτε να εφαρμόσετε αποτελεσματικά το i18n:
1. Δρομολόγηση με Υπο-διαδρομές (Sub-path Routing)
Οργανώστε τις διαδρομές σας με βάση τη γλώσσα (locale) χρησιμοποιώντας υπο-διαδρομές. Για παράδειγμα:
app/
[locale]/
page.tsx // Αρχική σελίδα για τη γλώσσα
about/
page.tsx // Σελίδα "Σχετικά με" για τη γλώσσα
// app/[locale]/page.tsx
import { getTranslations } from './dictionaries';
export default async function HomePage({ params: { locale } }) {
const t = await getTranslations(locale);
return (<h1>{t.home.title}</h1>);
}
// dictionaries.js
const dictionaries = {
en: () => import('./dictionaries/en.json').then((module) => module.default),
es: () => import('./dictionaries/es.json').then((module) => module.default),
};
export const getTranslations = async (locale) => {
try {
return dictionaries[locale]() ?? dictionaries.en();
} catch (error) {
console.error(`Αποτυχία φόρτωσης μεταφράσεων για τη γλώσσα ${locale}`, error);
return dictionaries.en();
}
};
Σε αυτή τη ρύθμιση, το δυναμικό τμήμα διαδρομής `[locale]` χειρίζεται διαφορετικές γλώσσες (π.χ., `/en`, `/es`). Οι μεταφράσεις φορτώνονται δυναμικά με βάση τη γλώσσα.
2. Δρομολόγηση με Domain
Για μια πιο προηγμένη προσέγγιση, μπορείτε να χρησιμοποιήσετε διαφορετικά domains ή subdomains για κάθε γλώσσα. Αυτό συχνά περιλαμβάνει πρόσθετη ρύθμιση στον πάροχο φιλοξενίας σας.
3. Middleware για Ανίχνευση Γλώσσας
Χρησιμοποιήστε middleware για να ανιχνεύσετε αυτόματα την προτιμώμενη γλώσσα του χρήστη και να τον ανακατευθύνετε ανάλογα.
// middleware.js
import { NextResponse } from 'next/server';
import { match } from '@formatjs/intl-localematcher';
import Negotiator from 'negotiator';
let locales = ['en', 'es', 'fr'];
function getLocale(request) {
const negotiatorHeaders = {};
request.headers.forEach((value, key) => (negotiatorHeaders[key] = value));
let languages = new Negotiator({ headers: negotiatorHeaders }).languages();
try {
return match(languages, locales, 'en'); // Χρήση του "en" ως προεπιλεγμένη γλώσσα
} catch (error) {
console.error("Σφάλμα αντιστοίχισης γλώσσας:", error);
return 'en'; // Επιστροφή στα Αγγλικά αν η αντιστοίχιση αποτύχει
}
}
export function middleware(request) {
const pathname = request.nextUrl.pathname;
const pathnameIsMissingLocale = locales.every(
(locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
);
if (pathnameIsMissingLocale) {
const locale = getLocale(request);
return NextResponse.redirect(
new URL(
`/${locale}${pathname.startsWith('/') ? '' : '/'}${pathname}`,
request.url
)
);
}
}
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
};
Αυτό το middleware ελέγχει εάν η αιτούμενη διαδρομή έχει πρόθεμα γλώσσας. Εάν όχι, ανιχνεύει την προτιμώμενη γλώσσα του χρήστη χρησιμοποιώντας την κεφαλίδα `Accept-Language` και τον ανακατευθύνει στην κατάλληλη διαδρομή για τη συγκεκριμένη γλώσσα. Βιβλιοθήκες όπως οι `@formatjs/intl-localematcher` και `negotiator` χρησιμοποιούνται για τον χειρισμό της διαπραγμάτευσης γλώσσας.
Next.js App Router και Παγκόσμια Προσβασιμότητα
Η δημιουργία παγκοσμίως προσβάσιμων εφαρμογών ιστού απαιτεί προσεκτική εξέταση των αρχών της προσβασιμότητας (a11y). Ο Next.js App Router παρέχει μια σταθερή βάση για τη δημιουργία προσβάσιμων εμπειριών, αλλά είναι απαραίτητο να εφαρμόζετε βέλτιστες πρακτικές για να διασφαλίσετε ότι η εφαρμογή σας είναι χρησιμοποιήσιμη από όλους, ανεξάρτητα από τις ικανότητές τους.
Βασικά Σημεία Προσβασιμότητας
- Σημασιολογική HTML: Χρησιμοποιήστε σημασιολογικά στοιχεία HTML (π.χ., `<article>`, `<nav>`, `<aside>`, `<main>`) για να δομήσετε το περιεχόμενό σας. Αυτό δίνει νόημα στις υποστηρικτικές τεχνολογίες και βοηθά τους χρήστες να πλοηγούνται στον ιστότοπό σας ευκολότερα.
- Χαρακτηριστικά ARIA: Χρησιμοποιήστε χαρακτηριστικά ARIA (Accessible Rich Internet Applications) για να βελτιώσετε την προσβασιμότητα των προσαρμοσμένων components και widgets. Τα χαρακτηριστικά ARIA παρέχουν πρόσθετες πληροφορίες σχετικά με τον ρόλο, την κατάσταση και τις ιδιότητες των στοιχείων στις υποστηρικτικές τεχνολογίες.
- Πλοήγηση με Πληκτρολόγιο: Βεβαιωθείτε ότι όλα τα διαδραστικά στοιχεία είναι προσβάσιμα μέσω πληκτρολογίου. Οι χρήστες θα πρέπει να μπορούν να πλοηγούνται στην εφαρμογή σας χρησιμοποιώντας το πλήκτρο `Tab` και να αλληλεπιδρούν με τα στοιχεία χρησιμοποιώντας τα πλήκτρα `Enter` ή `Space`.
- Αντίθεση Χρωμάτων: Χρησιμοποιήστε επαρκή αντίθεση χρωμάτων μεταξύ κειμένου και φόντου για να διασφαλίσετε την αναγνωσιμότητα για χρήστες με προβλήματα όρασης. Οι Οδηγίες Προσβασιμότητας Περιεχομένου Ιστού (WCAG) συνιστούν λόγο αντίθεσης τουλάχιστον 4.5:1 για κανονικό κείμενο και 3:1 για μεγάλο κείμενο.
- Εναλλακτικό Κείμενο Εικόνας (Alt Text): Παρέχετε περιγραφικό εναλλακτικό κείμενο για όλες τις εικόνες. Το alt text παρέχει μια εναλλακτική λύση κειμένου για τις εικόνες που μπορεί να διαβαστεί από τους αναγνώστες οθόνης.
- Ετικέτες Φόρμας: Συνδέστε τις ετικέτες φόρμας με τα αντίστοιχα πεδία εισαγωγής χρησιμοποιώντας το στοιχείο `<label>`. Αυτό καθιστά σαφές στους χρήστες ποιες πληροφορίες αναμένονται σε κάθε πεδίο.
- Δοκιμές με Αναγνώστη Οθόνης: Δοκιμάστε την εφαρμογή σας με έναν αναγνώστη οθόνης για να διασφαλίσετε ότι είναι προσβάσιμη σε χρήστες με προβλήματα όρασης. Δημοφιλείς αναγνώστες οθόνης περιλαμβάνουν τους NVDA, JAWS και VoiceOver.
Υλοποίηση Προσβασιμότητας στον Next.js App Router
- Χρήση του Next.js Link Component: Χρησιμοποιήστε το component `<Link>` για την πλοήγηση. Παρέχει ενσωματωμένες δυνατότητες προσβασιμότητας, όπως το prefetching και η διαχείριση της εστίασης (focus management).
- Διαχείριση Εστίασης: Κατά την πλοήγηση μεταξύ σελίδων ή το άνοιγμα modals, βεβαιωθείτε ότι η εστίαση διαχειρίζεται σωστά. Η εστίαση πρέπει να τίθεται στο πιο λογικό στοιχείο της νέας σελίδας ή του modal.
- Προσβάσιμα Προσαρμοσμένα Components: Όταν δημιουργείτε προσαρμοσμένα components, βεβαιωθείτε ότι είναι προσβάσιμα ακολουθώντας τις αρχές που περιγράφονται παραπάνω. Χρησιμοποιήστε σημασιολογική HTML, χαρακτηριστικά ARIA και πλοήγηση με πληκτρολόγιο για να κάνετε τα components σας χρησιμοποιήσιμα από όλους.
- Linting και Δοκιμές: Χρησιμοποιήστε εργαλεία linting όπως το ESLint με plugins προσβασιμότητας για να εντοπίσετε πιθανά προβλήματα προσβασιμότητας στον κώδικά σας. Επίσης, χρησιμοποιήστε αυτοματοποιημένα εργαλεία δοκιμών για να ελέγξετε την εφαρμογή σας για παραβιάσεις προσβασιμότητας.
Συμπέρασμα
Το σύστημα δρομολόγησης βάσει αρχείων του Next.js App Router προσφέρει έναν ισχυρό και διαισθητικό τρόπο για τη δόμηση και την πλοήγηση στις εφαρμογές σας. Κατανοώντας τις βασικές έννοιες και τις βέλτιστες πρακτικές που περιγράφονται σε αυτόν τον οδηγό, μπορείτε να δημιουργήσετε στιβαρές, επεκτάσιμες και συντηρήσιμες εφαρμογές Next.js. Πειραματιστείτε με τις διάφορες δυνατότητες του App Router και ανακαλύψτε πώς μπορεί να απλοποιήσει τη ροή εργασίας σας στην ανάπτυξη και να βελτιώσει την εμπειρία του χρήστη.