رابط (Link)
<Link>
هو مكون React يمتد عنصر HTML <a>
لتوفير الجلب المسبق (Prefetching) والتنقل من جانب العميل بين المسارات. وهو الطريقة الأساسية للتنقل بين المسارات في Next.js.
الاستخدام الأساسي:
import Link from 'next/link'
export default function Page() {
return <Link href="/dashboard">لوحة التحكم</Link>
}
import Link from 'next/link'
export default function Page() {
return <Link href="/dashboard">لوحة التحكم</Link>
}
المرجع
يمكن تمرير الخصائص التالية إلى مكون <Link>
:
الخاصية | مثال | النوع | مطلوب |
---|---|---|---|
href | href="/dashboard" | نص أو كائن | نعم |
replace | replace={false} | منطقي | - |
scroll | scroll={false} | منطقي | - |
prefetch | prefetch={false} | منطقي أو null | - |
onNavigate | onNavigate={(e) => {}} | دالة | - |
معلومة مفيدة: يمكن إضافة سمات
<a>
مثلclassName
أوtarget="_blank"
إلى<Link>
كخصائص وسيتم تمريرها إلى عنصر<a>
الأساسي.
href
(مطلوب)
المسار أو URL للانتقال إليه.
import Link from 'next/link'
// الانتقال إلى /about?name=test
export default function Page() {
return (
<Link
href={{
pathname: '/about',
query: { name: 'test' },
}}
>
حول
</Link>
)
}
import Link from 'next/link'
// الانتقال إلى /about?name=test
export default function Page() {
return (
<Link
href={{
pathname: '/about',
query: { name: 'test' },
}}
>
حول
</Link>
)
}
replace
القيمة الافتراضية false
. عند true
، سيستبدل next/link
حالة التاريخ الحالية بدلاً من إضافة URL جديد إلى تاريخ المتصفح.
import Link from 'next/link'
export default function Page() {
return (
<Link href="/dashboard" replace>
لوحة التحكم
</Link>
)
}
import Link from 'next/link'
export default function Page() {
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 Page() {
return (
<Link href="/dashboard" scroll={false}>
لوحة التحكم
</Link>
)
}
import Link from 'next/link'
export default function Page() {
return (
<Link href="/dashboard" scroll={false}>
لوحة التحكم
</Link>
)
}
prefetch
يحدث الجلب المسبق (Prefetching) عندما يدخل مكون <Link />
نافذة عرض المستخدم (مبدئيًا أو من خلال التمرير). يقوم Next.js بجلب وتحميل المسار المرتبط (المشار إليه بواسطة href
) وبياناته في الخلفية لتحسين أداء التنقل من جانب العميل. إذا انتهت صلاحية البيانات التي تم جلبها مسبقًا بحلول وقت تحويم المستخدم فوق <Link />
، فسيحاول Next.js جلبها مرة أخرى. الجلب المسبق ممكّن فقط في بيئة الإنتاج.
يمكن تمرير القيم التالية إلى خاصية prefetch
:
null
(افتراضي): يعتمد سلوك الجلب المسبق على ما إذا كان المسار ثابتًا أو ديناميكيًا. بالنسبة للمسارات الثابتة، سيتم جلب المسار بالكامل (بما في ذلك جميع بياناته). بالنسبة للمسارات الديناميكية، سيتم جلب جزء المسار حتى أقرب مقطع به حدloading.js
.true
: سيتم جلب المسار بالكامل لكل من المسارات الثابتة والديناميكية.false
: لن يحدث الجلب المسبق أبدًا سواء عند الدخول إلى نافذة العرض أو عند التحويم.
import Link from 'next/link'
export default function Page() {
return (
<Link href="/dashboard" prefetch={false}>
لوحة التحكم
</Link>
)
}
import Link from 'next/link'
export default function Page() {
return (
<Link href="/dashboard" prefetch={false}>
لوحة التحكم
</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>
في سيناريوهات مختلفة.
الربط مع المقاطع الديناميكية
عند الربط مع المقاطع الديناميكية، يمكنك استخدام القوالب الحرفية والاستيفاء لإنشاء قائمة من الروابط. على سبيل المثال، لإنشاء قائمة من منشورات المدونة:
import Link from 'next/link'
interface Post {
id: number
title: string
slug: string
}
export default function PostList({ posts }: { posts: Post[] }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
)
}
import Link from 'next/link'
export default function PostList({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
)
}
التحقق من الروابط النشطة
يمكنك استخدام usePathname()
لتحديد ما إذا كان الرابط نشطًا. على سبيل المثال، لإضافة فئة إلى الرابط النشط، يمكنك التحقق مما إذا كان pathname
الحالي يتطابق مع href
الرابط:
التمرير إلى id
إذا كنت ترغب في التمرير إلى id
معين عند التنقل، يمكنك إضافة رابط #
هاش إلى عنوان URL الخاص بك أو تمرير رابط هاش إلى خاصية href
. هذا ممكن لأن <Link>
يتم عرضه كعنصر <a>
.
<Link href="/dashboard#settings">Settings</Link>
// Output
<a href="/dashboard#settings">Settings</a>
معلومة مفيدة:
- سيقوم Next.js بالتمرير إلى الصفحة إذا لم تكن مرئية في نافذة العرض عند التنقل.
الربط بمقاطع المسار الديناميكي
بالنسبة لمقاطع المسار الديناميكية، يمكن أن يكون استخدام القوالب الحرفية مفيدًا لإنشاء مسار الرابط.
على سبيل المثال، يمكنك إنشاء قائمة من الروابط إلى المسار الديناميكي app/blog/[slug]/page.js
:
import Link from 'next/link'
export default function Page({ 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'
export default function Page({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
)
}
إذا كان العنصر الفرعي مكونًا مخصصًا يلف علامة <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 Page() {
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 Page() {
return (
<Link href="/about" passHref legacyBehavior>
<MyButton />
</Link>
)
}
استبدال URL بدلاً من الدفع
السلوك الافتراضي لمكون Link
هو دفع
عنوان URL جديد إلى مكدس history
. يمكنك استخدام خاصية replace
لمنع إضافة إدخال جديد، كما في المثال التالي:
import Link from 'next/link'
export default function Page() {
return (
<Link href="/about" replace>
About us
</Link>
)
}
import Link from 'next/link'
export default function Page() {
return (
<Link href="/about" replace>
About us
</Link>
)
}
تعطيل التمرير إلى أعلى الصفحة
السلوك الافتراضي للتمرير لـ <Link>
في Next.js هو الحفاظ على موضع التمرير، مشابه لكيفية تعامل المتصفحات مع التنقل للخلف وللأمام. عند الانتقال إلى صفحة جديدة، سيظل موضع التمرير كما هو طالما أن الصفحة مرئية في نافذة العرض.
ومع ذلك، إذا لم تكن الصفحة مرئية في نافذة العرض، فسوف يقوم Next.js بالتمرير إلى أعلى عنصر الصفحة الأول. إذا كنت ترغب في تعطيل هذا السلوك، يمكنك تمرير scroll={false}
إلى مكون <Link>
، أو scroll: false
إلى router.push()
أو router.replace()
.
import Link from 'next/link'
export default function Page() {
return (
<Link href="/#hashid" scroll={false}>
Disables scrolling to the top
</Link>
)
}
import Link from 'next/link'
export default function Page() {
return (
<Link href="/#hashid" scroll={false}>
Disables scrolling to the top
</Link>
)
}
استخدام router.push()
أو router.replace()
:
// useRouter
import { useRouter } from 'next/navigation'
const router = useRouter()
router.push('/dashboard', { scroll: false })
جلب الروابط مسبقًا في 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 Page() {
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 Page() {
const isAuthed = useIsAuthed()
const path = isAuthed ? '/auth/dashboard' : '/public/dashboard'
return (
<Link as="/dashboard" href={path}>
Dashboard
</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 . |