Link
<Link>
هو مكون React يمتد عنصر HTML <a>
لتوفير الجلب المسبق (Prefetching) والتنقل من جانب العميل بين المسارات. وهو الطريقة الأساسية للتنقل بين المسارات في Next.js.
الاستخدام الأساسي:
import Link from 'next/link'
export default function Home() {
return <Link href="/dashboard">لوحة التحكم</Link>
}
import Link from 'next/link'
export default function Home() {
return <Link href="/dashboard">لوحة التحكم</Link>
}
المرجع
يمكن تمرير الخصائص التالية إلى مكون <Link>
:
الخاصية | مثال | النوع | مطلوب |
---|---|---|---|
href | href="/dashboard" | نص أو كائن | نعم |
replace | replace={false} | منطقي | - |
scroll | scroll={false} | منطقي | - |
prefetch | prefetch={false} | منطقي | - |
legacyBehavior | legacyBehavior={true} | منطقي | - |
passHref | passHref={true} | منطقي | - |
shallow | shallow={false} | منطقي | - |
locale | locale="fr" | نص أو منطقي | - |
onNavigate | onNavigate={(e) => {}} | دالة | - |
معلومة مفيدة: يمكن إضافة سمات
<a>
مثلclassName
أوtarget="_blank"
إلى<Link>
كخصائص وسيتم تمريرها إلى عنصر<a>
الأساسي.
href
(مطلوب)
المسار أو URL للانتقال إليه.
import Link from 'next/link'
// الانتقال إلى /about?name=test
export default function Home() {
return (
<Link
href={{
pathname: '/about',
query: { name: 'test' },
}}
>
حول
</Link>
)
}
import Link from 'next/link'
// الانتقال إلى /about?name=test
export default function Home() {
return (
<Link
href={{
pathname: '/about',
query: { name: 'test' },
}}
>
حول
</Link>
)
}
replace
القيمة الافتراضية false
. عند true
، سيستبدل next/link
حالة التاريخ الحالية بدلاً من إضافة URL جديد إلى تاريخ المتصفح.
import Link from 'next/link'
export default function Home() {
return (
<Link href="/dashboard" replace>
لوحة التحكم
</Link>
)
}
import Link from 'next/link'
export default function Home() {
return (
<Link href="/dashboard" replace>
لوحة التحكم
</Link>
)
}
scroll
القيمة الافتراضية true
. السلوك الافتراضي للتمرير في <Link>
في Next.js هو الحفاظ على موضع التمرير، مشابه لكيفية تعامل المتصفحات مع التنقل للخلف وللأمام. عند الانتقال إلى صفحة جديدة، سيظل موضع التمرير كما هو طالما أن الصفحة مرئية في نافذة العرض. ومع ذلك، إذا لم تكن الصفحة مرئية في نافذة العرض، فسوف يقوم Next.js بالتمرير إلى أعلى أول عنصر في الصفحة.
عند scroll = {false}
، لن يحاول Next.js التمرير إلى أول عنصر في الصفحة.
معلومة مفيدة: يتحقق Next.js من
scroll: false
قبل إدارة سلوك التمرير. إذا كان التمرير ممكّنًا، فإنه يحدد عقدة DOM ذات الصلة بالتنقل ويتفحص كل عنصر من المستوى الأعلى. يتم تخطي جميع العناصر غير القابلة للتمرير وتلك التي لا تحتوي على HTML معروض، بما في ذلك العناصر ذات الموضع الثابت أو المثبت، والعناصر غير المرئية مثل تلك المحسوبة باستخدامgetBoundingClientRect
. ثم يستمر Next.js عبر العناصر الشقيقة حتى يحدد عنصرًا قابلًا للتمرير مرئيًا في نافذة العرض.
import Link from 'next/link'
export default function Home() {
return (
<Link href="/dashboard" scroll={false}>
لوحة التحكم
</Link>
)
}
import Link from 'next/link'
export default function Home() {
return (
<Link href="/dashboard" scroll={false}>
لوحة التحكم
</Link>
)
}
prefetch
يحدث الجلب المسبق (Prefetching) عندما يدخل مكون <Link />
نافذة عرض المستخدم (مبدئيًا أو من خلال التمرير). يقوم Next.js بجلب وتحميل المسار المرتبط (المشار إليه بواسطة href
) والبيانات في الخلفية لتحسين أداء التنقل من جانب العميل. الجلب المسبق ممكّن فقط في بيئة الإنتاج.
يمكن تمرير القيم التالية إلى خاصية prefetch
:
true
(افتراضي): سيتم جلب المسار بالكامل وبياناته مسبقًا.false
: لن يحدث الجلب المسبق عند الدخول إلى نافذة العرض، ولكن سيحدث عند التحويم. إذا كنت تريد إزالة الجلب تمامًا عند التحويم أيضًا، ففكر في استخدام علامة<a>
أو التبني التدريجي لموجه التطبيق، الذي يمكّن تعطيل الجلب المسبق عند التحويم أيضًا.
import Link from 'next/link'
export default function Home() {
return (
<Link href="/dashboard" prefetch={false}>
لوحة التحكم
</Link>
)
}
import Link from 'next/link'
export default function Home() {
return (
<Link href="/dashboard" prefetch={false}>
لوحة التحكم
</Link>
)
}
legacyBehavior
تحذير: سيتم إزالة خاصية
legacyBehavior
في Next.js v16. لتبني سلوك<Link>
الجديد، قم بإزالة أي علامات<a>
تستخدم كأبناء لـ<Link>
. يتوفر أداة تعديل تلقائي (codemod) لمساعدتك في ترقية قاعدة التعليمات البرمجية تلقائيًا.
بدءًا من الإصدار 13، لم يعد عنصر <a>
مطلوبًا كابن لمكون <Link>
. إذا كنت لا تزال بحاجة إلى السلوك القديم لأسباب التوافق، يمكنك إضافة خاصية legacyBehavior
.
معلومة مفيدة: عندما لا يتم تعيين
legacyBehavior
إلىtrue
، يمكن تمرير جميع خصائص علامة الربط (anchor) إلىnext/link
أيضًا مثلclassName
،onClick
، إلخ.
passHref
يجبر Link
على إرسال خاصية href
إلى ابنه. القيمة الافتراضية false
. راجع مثال تمرير مكون وظيفي لمزيد من المعلومات.
shallow
تحديث مسار الصفحة الحالية دون إعادة تشغيل getStaticProps
، getServerSideProps
أو getInitialProps
. القيمة الافتراضية false
.
import Link from 'next/link'
export default function Home() {
return (
<Link href="/dashboard" shallow={false}>
لوحة التحكم
</Link>
)
}
import Link from 'next/link'
export default function Home() {
return (
<Link href="/dashboard" shallow={false}>
لوحة التحكم
</Link>
)
}
locale
يتم إضافة اللغة النشطة تلقائيًا. تسمح locale
بتوفير لغة مختلفة. عند false
يجب أن يتضمن href
اللغة حيث يتم تعطيل السلوك الافتراضي.
import Link from 'next/link'
export default function Home() {
return (
<>
{/* السلوك الافتراضي: يتم إضافة اللغة */}
<Link href="/dashboard">لوحة التحكم (مع اللغة)</Link>
{/* تعطيل إضافة اللغة */}
<Link href="/dashboard" locale={false}>
لوحة التحكم (بدون لغة)
</Link>
{/* تحديد لغة مختلفة */}
<Link href="/dashboard" locale="fr">
لوحة التحكم (بالفرنسية)
</Link>
</>
)
}
import Link from 'next/link'
export default function Home() {
return (
<>
{/* السلوك الافتراضي: يتم إضافة اللغة */}
<Link href="/dashboard">لوحة التحكم (مع اللغة)</Link>
{/* تعطيل إضافة اللغة */}
<Link href="/dashboard" locale={false}>
لوحة التحكم (بدون لغة)
</Link>
{/* تحديد لغة مختلفة */}
<Link href="/dashboard" locale="fr">
لوحة التحكم (بالفرنسية)
</Link>
</>
)
}
onNavigate
معالج حدث يتم استدعاؤه أثناء التنقل من جانب العميل. يتلقى المعالج كائن حدث يتضمن طريقة preventDefault()
، مما يسمح لك بإلغاء التنقل إذا لزم الأمر.
import Link from 'next/link'
export default function Page() {
return (
<Link
href="/dashboard"
onNavigate={(e) => {
// ينفذ فقط أثناء التنقل SPA
console.log('جارٍ التنقل...')
// إلغاء التنقل اختياريًا
// e.preventDefault()
}}
>
لوحة التحكم
</Link>
)
}
import Link from 'next/link'
export default function Page() {
return (
<Link
href="/dashboard"
onNavigate={(e) => {
// ينفذ فقط أثناء التنقل SPA
console.log('جارٍ التنقل...')
// إلغاء التنقل اختياريًا
// e.preventDefault()
}}
>
لوحة التحكم
</Link>
)
}
معلومة مفيدة: بينما قد يبدو
onClick
وonNavigate
متشابهين، إلا أنهما يخدمان أغراضًا مختلفة. ينفذonClick
لجميع أحداث النقر، بينما يعملonNavigate
فقط أثناء التنقل من جانب العميل. بعض الاختلافات الرئيسية:
- عند استخدام مفاتيح التعديل (
Ctrl
/Cmd
+ النقر)، ينفذonClick
ولكنonNavigate
لا ينفذ لأن Next.js يمنع التنقل الافتراضي لعلامات التبويب الجديدة.- لن تؤدي عناوين URL الخارجية إلى تشغيل
onNavigate
لأنها مخصصة فقط للتنقل من جانب العميل ونفس المصدر.- ستعمل الروابط ذات سمة
download
معonClick
ولكن ليس معonNavigate
لأن المتصفح سيعامل عنوان URL المرتبط كتنزيل.
أمثلة
توضح الأمثلة التالية كيفية استخدام مكون <Link>
في سيناريوهات مختلفة.
الربط بمقاطع المسار الديناميكي
بالنسبة لمقاطع المسار الديناميكية، يمكن أن يكون استخدام القوالب الحرفية مفيدًا لإنشاء مسار الرابط.
على سبيل المثال، يمكنك إنشاء قائمة من الروابط إلى المسار الديناميكي pages/blog/[slug].js
import Link from 'next/link'
function Posts({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
)
}
import Link from 'next/link'
function Posts({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
)
}
export default Posts
إذا كان العنصر الفرعي مكونًا مخصصًا يلف علامة <a>
إذا كان العنصر الفرعي لـ Link
هو مكون مخصص يلف علامة <a>
، فيجب عليك إضافة passHref
إلى Link
. هذا ضروري إذا كنت تستخدم مكتبات مثل styled-components. بدون ذلك، لن تحتوي علامة <a>
على سمة href
، مما يؤثر على إمكانية الوصول إلى موقعك وقد يؤثر على تحسين محركات البحث (SEO). إذا كنت تستخدم ESLint، فهناك قاعدة مدمجة next/link-passhref
لضمان الاستخدام الصحيح لـ passHref
.
- إذا كنت تستخدم ميزة JSX pragma لـ emotion (
@jsx jsx
)، فيجب عليك استخدامpassHref
حتى إذا كنت تستخدم علامة<a>
مباشرة. - يجب أن يدعم المكون خاصية
onClick
لتنشيط التنقل بشكل صحيح.
تداخل مكون وظيفي
إذا كان العنصر الفرعي لـ Link
هو مكون وظيفي، بالإضافة إلى استخدام passHref
و legacyBehavior
، يجب عليك لف المكون في React.forwardRef
:
import Link from 'next/link'
import React from 'react'
// تعريف نوع الخصائص لـ MyButton
interface MyButtonProps {
onClick?: React.MouseEventHandler<HTMLAnchorElement>
href?: string
}
// استخدام React.ForwardRefRenderFunction لكتابة المرجع المقدم بشكل صحيح
const MyButton: React.ForwardRefRenderFunction<
HTMLAnchorElement,
MyButtonProps
> = ({ onClick, href }, ref) => {
return (
<a href={href} onClick={onClick} ref={ref}>
Click Me
</a>
)
}
// استخدام React.forwardRef لف المكون
const ForwardedMyButton = React.forwardRef(MyButton)
export default function Home() {
return (
<Link href="/about" passHref legacyBehavior>
<ForwardedMyButton />
</Link>
)
}
import Link from 'next/link'
import React from 'react'
// `onClick`، `href`، و `ref` يجب تمريرها إلى عنصر DOM
// للمعالجة الصحيحة
const MyButton = React.forwardRef(({ onClick, href }, ref) => {
return (
<a href={href} onClick={onClick} ref={ref}>
Click Me
</a>
)
})
// إضافة اسم عرض للمكون (مفيد لتصحيح الأخطاء)
MyButton.displayName = 'MyButton'
export default function Home() {
return (
<Link href="/about" passHref legacyBehavior>
<MyButton />
</Link>
)
}
تمرير كائن URL
يمكن لـ Link
أيضًا استقبال كائن URL وسيقوم تلقائيًا بتنسيقه لإنشاء سلسلة URL:
import Link from 'next/link'
function Home() {
return (
<ul>
<li>
<Link
href={{
pathname: '/about',
query: { name: 'test' },
}}
>
About us
</Link>
</li>
<li>
<Link
href={{
pathname: '/blog/[slug]',
query: { slug: 'my-post' },
}}
>
Blog Post
</Link>
</li>
</ul>
)
}
export default Home
import Link from 'next/link'
function Home() {
return (
<ul>
<li>
<Link
href={{
pathname: '/about',
query: { name: 'test' },
}}
>
About us
</Link>
</li>
<li>
<Link
href={{
pathname: '/blog/[slug]',
query: { slug: 'my-post' },
}}
>
Blog Post
</Link>
</li>
</ul>
)
}
export default Home
يحتوي المثال أعلاه على رابط إلى:
- مسار محدد مسبقًا:
/about?name=test
- مسار ديناميكي:
/blog/my-post
يمكنك استخدام كل خاصية كما هو محدد في توثيق وحدة URL لـ Node.js.
استبدال URL بدلاً من الدفع
السلوك الافتراضي لمكون Link
هو دفع
عنوان URL جديد إلى مكدس history
. يمكنك استخدام خاصية replace
لمنع إضافة إدخال جديد، كما في المثال التالي:
import Link from 'next/link'
export default function Home() {
return (
<Link href="/about" replace>
About us
</Link>
)
}
import Link from 'next/link'
export default function Home() {
return (
<Link href="/about" replace>
About us
</Link>
)
}
تعطيل التمرير إلى أعلى الصفحة
السلوك الافتراضي لـ Link
هو التمرير إلى أعلى الصفحة. عندما يكون هناك هاش معرف، سوف يمرر إلى المعرف المحدد، مثل علامة <a>
العادية. لمنع التمرير إلى الأعلى / الهاش، يمكن إضافة scroll={false}
إلى Link
:
import Link from 'next/link'
export default function Home() {
return (
<Link href="/#hashid" scroll={false}>
Disables scrolling to the top
</Link>
)
}
import Link from 'next/link'
export default function Home() {
return (
<Link href="/#hashid" scroll={false}>
Disables scrolling to the top
</Link>
)
}
جلب الروابط مسبقًا في Middleware
من الشائع استخدام Middleware للمصادقة أو أغراض أخرى تتضمن إعادة توجيه المستخدم إلى صفحة مختلفة. لكي يعمل مكون <Link />
بشكل صحيح في جلب الروابط مسبقًا مع إعادة التوجيه عبر Middleware، تحتاج إلى إخبار Next.js بكل من عنوان URL المعروض وعنوان URL المطلوب جلبها مسبقًا. هذا مطلوب لتجنب طلبات غير ضرورية إلى middleware لمعرفة المسار الصحيح الذي يجب جلبها مسبقًا.
على سبيل المثال، إذا كنت تريد تقديم مسار /dashboard
الذي يحتوي على واجهات للمستخدمين المصادق عليهم والزوار، يمكنك إضافة ما يلي في Middleware الخاص بك لإعادة توجيه المستخدم إلى الصفحة الصحيحة:
import { NextResponse } from 'next/server'
export function middleware(request: Request) {
const nextUrl = request.nextUrl
if (nextUrl.pathname === '/dashboard') {
if (request.cookies.authToken) {
return NextResponse.rewrite(new URL('/auth/dashboard', request.url))
} else {
return NextResponse.rewrite(new URL('/public/dashboard', request.url))
}
}
}
import { NextResponse } from 'next/server'
export function middleware(request) {
const nextUrl = request.nextUrl
if (nextUrl.pathname === '/dashboard') {
if (request.cookies.authToken) {
return NextResponse.rewrite(new URL('/auth/dashboard', request.url))
} else {
return NextResponse.rewrite(new URL('/public/dashboard', request.url))
}
}
}
في هذه الحالة، سترغب في استخدام الكود التالي في مكون <Link />
الخاص بك:
'use client'
import Link from 'next/link'
import useIsAuthed from './hooks/useIsAuthed' // خطاف المصادقة الخاص بك
export default function Home() {
const isAuthed = useIsAuthed()
const path = isAuthed ? '/auth/dashboard' : '/public/dashboard'
return (
<Link as="/dashboard" href={path}>
Dashboard
</Link>
)
}
'use client'
import Link from 'next/link'
import useIsAuthed from './hooks/useIsAuthed' // خطاف المصادقة الخاص بك
export default function Home() {
const isAuthed = useIsAuthed()
const path = isAuthed ? '/auth/dashboard' : '/public/dashboard'
return (
<Link as="/dashboard" href={path}>
Dashboard
</Link>
)
}
معلومة مفيدة: إذا كنت تستخدم المسارات الديناميكية (Dynamic Routes)، فستحتاج إلى تكييف خاصيتي
as
وhref
. على سبيل المثال، إذا كان لديك مسار ديناميكي مثل/dashboard/authed/[user]
تريد عرضه بشكل مختلف عبر middleware، ستكتب:<Link href={{ pathname: '/dashboard/authed/[user]', query: { user: username } }} as="/dashboard/[user]">Profile</Link>
.
منع التنقل
يمكنك استخدام خاصية onNavigate
لمنع التنقل عند استيفاء شروط معينة، مثل وجود تغييرات غير محفوظة في نموذج. عندما تحتاج إلى منع التنقل عبر عدة مكونات في تطبيقك (مثل منع التنقل من أي رابط أثناء تحرير نموذج)، يوفر React Context طريقة نظيفة لمشاركة حالة المنع هذه. أولاً، أنشئ سياقًا لتتبع حالة منع التنقل:
أنشئ مكون نموذج يستخدم السياق:
'use client'
import { useNavigationBlocker } from '../contexts/navigation-blocker'
export default function Form() {
const { setIsBlocked } = useNavigationBlocker()
return (
<form
onSubmit={(e) => {
e.preventDefault()
setIsBlocked(false)
}}
onChange={() => setIsBlocked(true)}
>
<input type="text" name="name" />
<button type="submit">Save</button>
</form>
)
}
'use client'
import { useNavigationBlocker } from '../contexts/navigation-blocker'
export default function Form() {
const { setIsBlocked } = useNavigationBlocker()
return (
<form
onSubmit={(e) => {
e.preventDefault()
setIsBlocked(false)
}}
onChange={() => setIsBlocked(true)}
>
<input type="text" name="name" />
<button type="submit">Save</button>
</form>
)
}
أنشئ مكون رابط مخصص يمنع التنقل:
'use client'
import Link from 'next/link'
import { useNavigationBlocker } from '../contexts/navigation-blocker'
interface CustomLinkProps extends React.ComponentProps<typeof Link> {
children: React.ReactNode
}
export function CustomLink({ children, ...props }: CustomLinkProps) {
const { isBlocked } = useNavigationBlocker()
return (
<Link
onNavigate={(e) => {
if (
isBlocked &&
!window.confirm('You have unsaved changes. Leave anyway?')
) {
e.preventDefault()
}
}}
{...props}
>
{children}
</Link>
)
}
'use client'
import Link from 'next/link'
import { useNavigationBlocker } from '../contexts/navigation-blocker'
export function CustomLink({ children, ...props }) {
const { isBlocked } = useNavigationBlocker()
return (
<Link
onNavigate={(e) => {
if (
isBlocked &&
!window.confirm('You have unsaved changes. Leave anyway?')
) {
e.preventDefault()
}
}}
{...props}
>
{children}
</Link>
)
}
أنشئ مكون تنقل:
أخيرًا، لف تطبيقك بـ NavigationBlockerProvider
في التخطيط الجذري واستخدم المكونات في صفحتك:
import { NavigationBlockerProvider } from './contexts/navigation-blocker'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<NavigationBlockerProvider>{children}</NavigationBlockerProvider>
</body>
</html>
)
}
import { NavigationBlockerProvider } from './contexts/navigation-blocker'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<NavigationBlockerProvider>{children}</NavigationBlockerProvider>
</body>
</html>
)
}
ثم استخدم مكونات Nav
و Form
في صفحتك:
import Nav from './components/nav'
import Form from './components/form'
export default function Page() {
return (
<div>
<Nav />
<main>
<h1>Welcome to the Dashboard</h1>
<Form />
</main>
</div>
)
}
import Nav from './components/nav'
import Form from './components/form'
export default function Page() {
return (
<div>
<Nav />
<main>
<h1>Welcome to the Dashboard</h1>
<Form />
</main>
</div>
)
}
عندما يحاول المستخدم التنقل بعيدًا باستخدام CustomLink
بينما يحتوي النموذج على تغييرات غير محفوظة، سيتم مطالبتهم بالتأكيد قبل المغادرة.
سجل الإصدارات
الإصدار | التغييرات |
---|---|
v15.3.0 | إضافة واجهة برمجة التطبيقات onNavigate |
v13.0.0 | لم يعد يتطلب علامة <a> فرعية. يتم توفير أداة تحويل الكود (codemod) لتحديث قاعدة الكود الخاصة بك تلقائيًا. |
v10.0.0 | يتم حل خاصية href التي تشير إلى مسار ديناميكي تلقائيًا ولم تعد تتطلب خاصية as . |
v8.0.0 | تحسين أداء الجلب المسبق. |
v1.0.0 | تم تقديم next/link . |