لینکسازی با Link
در درس «ساخت اسکلت اولیهی DevNotes» برای اولین بار از کامپوننت Link استفاده کردیم و گفتیم جزئیاتش را بعداً میبینیم. حالا وقتش است. لینک سادهترین راه حرکت کاربر بین صفحات است، اما در Next.js فقط یک تگ <a> ساده نیست؛ ناوبری سریع، Prefetch و حفظ Layout را هم به همراه دارد. در این درس یاد میگیریم Link چه کاری میکند، چطور آدرسهای مختلف را به آن بدهیم و چطور لینک «فعال» بسازیم.
پیشنیاز
- فصل «App Router، صفحات و Layoutها» و اسکلت پروژهی DevNotes
- آشنایی با تگ
<a>و ویژگیhrefدر HTML
چرا Link و نه تگ a؟
با تگ <a> معمولی، هر کلیک مرورگر را وادار میکند کل صفحه را از اول از سرور بگیرد و همهچیز را از نو بسازد. کامپوننت Link همین کار را بهتر انجام میدهد:
| ویژگی | تگ <a> |
کامپوننت Link |
|---|---|---|
| نوع ناوبری | بارگذاری کامل صفحه | ناوبری سمت کلاینت؛ فقط بخش تغییرکرده بهروز میشود |
| Layoutهای مشترک | از نو ساخته میشوند | حفظ میشوند |
| Prefetch (بارگیری پیشاپیش مسیر) | ندارد | دارد؛ جزئیاتش در درس «Prefetching و رفتار Navigation» |
| خروجی HTML | <a> |
همان <a> واقعی؛ برای SEO و دسترسپذیری مشکلی ایجاد نمیکند |
نکتهی مهم: Link خودش یک تگ <a> رندر میکند، پس لازم نیست داخل آن تگ <a> جداگانه بگذارید. این الگو در آموزشهای قدیمی دیده میشود و در نسخههای جدید لازم نیست.
استفادهی پایه
import Link from "next/link";
export default function Example() {
return <Link href="/posts">نوشتهها</Link>;
}Linkرا ازnext/linkوارد میکنیم.- ویژگی
hrefالزامی است و مسیر مقصد را میگیرد. - میتوان از
Linkهم در Server Component و هم در Client Component استفاده کرد؛ نیازی به"use client"نیست. - ویژگیهای معمول تگ
aمثلclassName،aria-*وidرا هم میپذیرد.
شکلهای مختلف href
۱. رشتهی ساده
<Link href="/about">درباره</Link>۲. مسیر پویا با Template String
وقتی بخشی از آدرس از داده میآید، مسیر را با Template String میسازیم. این الگو برای لینکدادن از فهرست نوشتهها به صفحهی هر نوشته کاربرد دارد؛ خود مسیر پویا را در درس بعد میسازیم:
type Post = { slug: string; title: string };
function PostLink({ post }: { post: Post }) {
return (
<Link href={`/posts/${post.slug}`}>
{post.title}
</Link>
);
}
اگر بخشی از آدرس ممکن است کاراکتر خاص (فاصله، / یا ?) داشته باشد، آن را با encodeURIComponent رمزگذاری کنید تا آدرس خراب نشود:
<Link href={`/posts?q=${encodeURIComponent(searchText)}`}>
جستوجو
</Link>۳. آبجکت URL
بهجای رشته میتوان یک آبجکت داد. این روش برای ساخت Query String خواناتر است:
<Link
href={{
pathname: "/posts",
query: { tag: "nextjs", page: 2 },
}}
>
نوشتههای Next.js
</Link>
این لینک به /posts?tag=nextjs&page=2 میرود. خواندن Query String در صفحه را در درس «searchParams و Query String» یاد میگیریم.
۴. لینک به بخشی از همان صفحه (Hash)
<Link href="/about#contact">تماس با ما</Link>ویژگیهای پرکاربرد Link
| ویژگی | کاربرد |
|---|---|
href |
مقصد لینک (رشته یا آبجکت)؛ الزامی است. |
replace |
بهجای افزودن یک رکورد جدید به تاریخچهی مرورگر، رکورد فعلی را جایگزین میکند؛ دکمهی «بازگشت» به صفحهی قبل از آن نمیرود. |
scroll |
پیشفرض true است و بعد از ناوبری به بالای صفحه میرود. با scroll={false} این رفتار خاموش میشود. |
prefetch |
کنترل Prefetch؛ مثلاً prefetch={false} آن را غیرفعال میکند. بهصورت پیشفرض، Next.js خودش تصمیم میگیرد. |
<Link href="/posts" replace>نوشتهها</Link>
<Link href="/posts" scroll={false}>نوشتهها</Link>
<Link href="/posts" prefetch={false}>نوشتهها</Link>
Link گزینههای دیگری هم دارد که کاربرد تخصصیتری دارند و در این دوره لازم نمیشوند.
لینک به بیرون از سایت
برای آدرسهای خارجی مزیت ناوبری سمت کلاینت وجود ندارد، پس تگ <a> معمولی انتخاب سادهتری است. اگر لینک را در تب جدید باز میکنید، rel مناسب را هم بنویسید:
<a
href="https://nextjs.org/docs"
target="_blank"
rel="noopener noreferrer"
>
مستندات Next.js
</a>لینک فعال (Active Link)
در منوی ناوبری معمولاً میخواهیم لینکِ صفحهی جاری مشخص باشد. برای این کار باید مسیر فعلی را بدانیم. Hook با نام usePathname این مسیر را میدهد، اما فقط در Client Component کار میکند. در فصل «Server Components و Client Components» دلیل این محدودیت را کامل میفهمیم؛ فعلاً فقط کافی است بدانیم فایلی که با "use client" شروع شود در مرورگر هم اجرا میشود.
یک کامپوننت کوچک میسازیم که فقط همین وظیفه را دارد:
// components/NavLink.tsx
"use client";
import type { ReactNode } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
type NavLinkProps = {
href: string;
children: ReactNode;
};
export default function NavLink({ href, children }: NavLinkProps) {
const pathname = usePathname();
const isActive =
href === "/"
? pathname === "/"
: pathname === href || pathname.startsWith(`${href}/`);
return (
<Link href={href} aria-current={isActive ? "page" : undefined}>
{children}
</Link>
);
}توضیح کد
"use client"در بالای فایل، آن را Client Component میکند؛ لازمهی استفاده ازusePathnameاست.usePathnameرا ازnext/navigationوارد میکنیم (نهnext/routerکه مربوط به Pages Router است).- برای صفحهی اصلی مقایسهی دقیق (
===) لازم است، وگرنه چون هر آدرسی با/شروع میشود، لینک «خانه» همیشه فعال میشد. - برای بقیهی لینکها، زیرمسیرها هم حساب میشوند: وقتی در
/posts/helloهستیم، لینک/postsفعال است. - ویژگی
aria-current="page"به صفحهخوانها میگوید این لینک صفحهی جاری است. فعلاً استایلی نداریم؛ بعداً میتوان با انتخابگر[aria-current="page"]آن را ظاهراً هم متمایز کرد.
عملی: استفادهی NavLink در هدر DevNotes
فایلهای متأثر: فایل جدید components/NavLink.tsx و تغییر components/SiteHeader.tsx. خود SiteHeader همچنان Server Component میماند و فقط NavLink کوچک در مرورگر اجرا میشود.
// components/SiteHeader.tsx
import NavLink from "@/components/NavLink";
export default function SiteHeader() {
return (
<header>
<strong>DevNotes</strong>
<nav aria-label="ناوبری اصلی">
<ul>
<li><NavLink href="/">خانه</NavLink></li>
<li><NavLink href="/posts">نوشتهها</NavLink></li>
<li><NavLink href="/about">درباره</NavLink></li>
<li><NavLink href="/login">ورود</NavLink></li>
</ul>
</nav>
</header>
);
}خروجی و رفتار مورد انتظار
- با کلیک روی لینکهای هدر، صفحه بدون بارگذاری کامل عوض میشود و هدر و فوتر از نو ساخته نمیشوند.
- برای بررسی ویژگی
aria-current، در مرورگر روی لینک صفحهی جاری کلیک راست و گزینهی «Inspect» را بزنید؛ در/postsبایدaria-current="page"فقط روی لینک «نوشتهها» دیده شود. - در ابزار Network مرورگر میبینید که هنگام ناوبری، بهجای دریافت کل سند HTML، درخواستهای کوچکتری فرستاده میشود.
نکتههای مهم
-
برای لینک داخلی همیشه Link: اگر بهجایش
<a href="/posts">بنویسید، ناوبری سریع از بین میرود و ESLint در Next.js هشدار میدهد. -
در Server Component فقط Hook نخواهید:
usePathnameوuseRouterفقط در Client Component کار میکنند. خودLinkاین محدودیت را ندارد. -
Link برای ناوبری کاربر است: اگر باید بعد از یک رویداد (مثلاً موفقیت ورود) از داخل کد به صفحهی دیگر بروید، از
useRouterیاredirectاستفاده میکنیم؛ موضوع درسهای «مسیریابی برنامهای با useRouter» و «Redirect، not-found.tsx و notFound()». - در حالت توسعه Prefetch محدود است: رفتار کامل Prefetch فقط در نسخهی Production دیده میشود. این را در درس «Prefetching و رفتار Navigation» بررسی میکنیم.
جمعبندی
Linkکامپوننتnext/linkاست که یک تگ<a>واقعی با ناوبری سریع سمت کلاینت و Prefetch میسازد.hrefمیتواند رشته (ثابت یا با Template String)، آبجکتpathname/queryیا آدرس همراه Hash باشد.- ویژگیهای
replace،scrollوprefetchرفتار ناوبری را تنظیم میکنند. - برای لینک به بیرون از سایت تگ
aمعمولی کافی است. - لینک فعال با
usePathnameدر یک Client Component کوچک ساخته میشود؛ در DevNotes این کار را باNavLinkانجام دادیم. - در درس بعد، «مسیرهای پویا، params ناهمگام و مسیرهای تودرتو»، مسیرهایی میسازیم که بخشی از آدرسشان متغیر است، مثل صفحهی هر نوشته.
