الفرق بين المراجعتين لصفحة: «Next.js/Routing»
لا ملخص تعديل |
جميل-بيلوني (نقاش | مساهمات) طلا ملخص تعديل |
||
(4 مراجعات متوسطة بواسطة مستخدمين اثنين آخرين غير معروضة) | |||
سطر 1: | سطر 1: | ||
<noinclude>{{DISPLAYTITLE:التوجيه routing في Next.js}}</noinclude> | |||
تمتلك موجهًا للمسارات يعتمد على نظام ملفات مبني على مفهوم [[Next.js/pages|الصفحات]] | تمتلك Next.js موجهًا للمسارات router يعتمد على نظام ملفات مبني على مفهوم [[Next.js/pages|الصفحات]]، فعندما يُضاف ملف إلى مجلد باسم pages فسيُتاح مباشرة كوجهة (أو مسار) route، ويمكن استخدام الملفات ضمن هذا المجلد لتعريف مختلف الأنماط المعروفة بالنسبة للوجهات. | ||
== أنماط الوجهات | == أنماط الوجهات == | ||
تدعم Next.js عددًا من أنماط الوجهات سنتعرف عليها | تدعم Next.js عددًا من أنماط الوجهات سنتعرف عليها تباعًا. | ||
=== الوجهات الخاصة بالصفحة Index === | === الوجهات الخاصة بالصفحة Index === | ||
سيحدد | سيحدد الموجّه تلقائيًا موقع الصفحات التي تُدعى <code>index</code> بالنسبة إلى المجلد الجذري كما في الأمثلة التالية:<syntaxhighlight lang="bash"> | ||
pages/index.js → / | pages/index.js → / | ||
سطر 14: | سطر 14: | ||
=== الوجهات المتداخلة === | === الوجهات المتداخلة === | ||
يدعم | يدعم الموجّه الملفات المتداخلة، فلو أنشأت هيكلية متداخلة لمجلد سيحدد الموجّه تلقائيًا موقع كل منها بنفس الطريقة السابقة:<syntaxhighlight lang="bash"> | ||
pages/blog/first-post.js → /blog/first-post | pages/blog/first-post.js → /blog/first-post | ||
سطر 21: | سطر 21: | ||
=== الأقسام الديناميكية لوجهة === | === الأقسام الديناميكية لوجهة === | ||
لمطابقة جزء ديناميكي من مسار بإمكانك استخدام صيغة الأقواس المربعة، وسيتيح لك ذلك مطابقة معاملات مسماة:<syntaxhighlight lang="bash"> | لمطابقة جزء ديناميكي من مسار بإمكانك استخدام صيغة الأقواس المربعة، وسيتيح لك ذلك مطابقة معاملات مسماة named parameters:<syntaxhighlight lang="bash"> | ||
pages/blog/[slug].js → /blog/:slug (/blog/hello-world) | pages/blog/[slug].js → /blog/:slug (/blog/hello-world) | ||
سطر 27: | سطر 27: | ||
pages/post/[...all].js → /post/* (/post/2020/id/title) | pages/post/[...all].js → /post/* (/post/2020/id/title) | ||
</syntaxhighlight> | </syntaxhighlight>سنتعرف أكثر على الوجهات الديناميكة تاليًا في صفحة التوثيق هذه. | ||
== الربط ما بين الصفحات | == الربط ما بين الصفحات == | ||
يتح لك موجّه Next.js الانتقال بين الصفحات في جانب العميل بشكل مشابهٍ للتطبيقات وحيدة الصفحة. يُستخدم مكوّن React الذي يُدعى <code>Link</code> في إنجاز هذا الأمر من جانب العميل:<syntaxhighlight lang="javascript"> | يتح لك موجّه Next.js الانتقال بين الصفحات في جانب العميل بشكل مشابهٍ للتطبيقات وحيدة الصفحة SPA اختصارًا إلى single-page application. يُستخدم مكوّن [[React]] الذي يُدعى <code>Link</code> في إنجاز هذا الأمر من جانب العميل:<syntaxhighlight lang="javascript"> | ||
import Link from 'next/link' | import Link from 'next/link' | ||
سطر 37: | سطر 37: | ||
<ul> | <ul> | ||
<li> | <li> | ||
<Link href="/" | <Link href="/">Home</Link> | ||
</li> | </li> | ||
<li> | <li> | ||
<Link href="/about" | <Link href="/about">About Us</Link> | ||
</li> | </li> | ||
<li> | <li> | ||
<Link href="/blog/hello-world" | <Link href="/blog/hello-world">Blog Post</Link> | ||
</li> | </li> | ||
</ul> | </ul> | ||
سطر 56: | سطر 50: | ||
export default Home | export default Home | ||
</syntaxhighlight>يستخدم المثال السابق روابط عدة يتعلق كل منها بمسار | </syntaxhighlight>يستخدم المثال السابق روابط عدة يتعلق كل منها بمسار <code>href</code> إلى صفحة معروفة:<syntaxhighlight lang="bash"> | ||
`/` → `pages/index.js` | `/` → `pages/index.js` | ||
سطر 62: | سطر 56: | ||
`/blog/hello-world` → `pages/blog/[slug].js` | `/blog/hello-world` → `pages/blog/[slug].js` | ||
</syntaxhighlight>إن كل مكوّن <code></ Link></code> في نافذة العرض (بشكلها الأساسي أو بعد تمرير محتويات الشاشة) سيُحضَر مسبقًا | </syntaxhighlight>إن كل مكوّن <code></ Link></code> في نافذة العرض viewport (بشكلها الأساسي أو بعد تمرير محتويات الشاشة) سيُحضَر مسبقًا prefetched بشكل افتراضي (بما في ذلك البيانات المتعلقة به) للصفحات التي تعتمد [[Next.js/data fetching|التوليد الساكن]] Static Generation أما الصفحات التي تعتمد على التصيير من [[Next.js/data fetching|جانب الخادم]] server-rendered فستُجلب '''بياناتها فقط''' عند الضغط على الرابط فقط. | ||
=== الارتباط بمسارات ديناميكية === | === الارتباط بمسارات ديناميكية === | ||
يمكن استخدام | يمكن استخدام إنشاء مسار بشكل ديناميكي، وتظهر الفائدة من ذلك عندما يتضمن عنوان المسار أجزاءً ديناميكة. فلو أردت أن تعرض على سبيل المثال مجموعة من المنشورات مع روابط لها مُرِّرت إلى المكوّن على شكل خاصية، فيمكن استعمال الشيفرة التالية مثلًا: <syntaxhighlight lang="javascript"> | ||
import Link from 'next/link' | import Link from 'next/link' | ||
سطر 74: | سطر 68: | ||
<li key={post.id}> | <li key={post.id}> | ||
<Link href={`/blog/${encodeURIComponent(post.slug)}`}> | <Link href={`/blog/${encodeURIComponent(post.slug)}`}> | ||
{post.title} | |||
</Link> | </Link> | ||
</li> | </li> | ||
سطر 83: | سطر 77: | ||
export default Posts | export default Posts | ||
</syntaxhighlight>'''ملاحظة''': استُخدم الكائن <code>encodeURIComponent</code> في المثال السابق للمحافظة على توافق المسار مع utf-8.<syntaxhighlight lang="javascript"> | </syntaxhighlight>'''ملاحظة''': استُخدم الكائن <code>encodeURIComponent</code> في المثال السابق للمحافظة على توافق المسار مع ترميز utf-8.<syntaxhighlight lang="javascript"> | ||
import Link from 'next/link' | import Link from 'next/link' | ||
سطر 97: | سطر 91: | ||
}} | }} | ||
> | > | ||
{post.title} | |||
</Link> | </Link> | ||
</li> | </li> | ||
سطر 106: | سطر 100: | ||
export default Posts | export default Posts | ||
</syntaxhighlight>بدلًا من استخدام طريقة | </syntaxhighlight>بدلًا من استخدام طريقة إنشاء المسار السابقة، استخدمنا في الحالة الثانية كائن URL كقيمة للخاصية <code>href</code>: | ||
* <code>pathname</code>: هو اسم الصفحة الموجودة في المجلد <code>pages</code>، وهي<code>blog/[slug]/</code> في مثالنا. | * <code>pathname</code>: هو اسم الصفحة الموجودة في المجلد <code>pages</code>، وهي<code>blog/[slug]/</code> في مثالنا. | ||
* <code>query</code>: | * <code>query</code>: هو كائن يحتوي على جزء ديناميكي، وهو <code>slug</code> في حالتنا. | ||
=== | === الوصول إلى كائن التوجيه <code>router</code> === | ||
للوصول إلى الكائن في مكوّن React، يمكنك استخدام [[Next.js/next router|<code>useRouter</code>]] أو [[Next.js/next router|<code>withRouter</code>]] علمًا أنه من الأفضل استخدام <code>useRouter</code> عمومًا. | للوصول إلى الكائن <code>[[Next.js/next router|router]]</code> في مكوّن React، يمكنك استخدام [[Next.js/next router|<code>useRouter</code>]] أو [[Next.js/next router|<code>withRouter</code>]] علمًا أنه من الأفضل استخدام <code>useRouter</code> عمومًا. | ||
== الوجهات الديناميكية | == الوجهات الديناميكية Dynamic Routes == | ||
لا يُعد تحديد المسارات مسبقًا أمرًا كافيًا للتطبيقات المعقدة، لهذا تتيح لك إمكانية إضافة أقواس مربعة حول صفحة (<code>[param]</code>) لإنشاء وجهة ديناميكة. | لا يُعد تحديد المسارات مسبقًا أمرًا كافيًا للتطبيقات المعقدة، لهذا تتيح لك Next.js إمكانية إضافة أقواس مربعة حول صفحة (<code>[param]</code>) لإنشاء وجهة ديناميكة. | ||
لنتأمل الصفحة التالية | لنتأمل الصفحة التالية <code>pages/post/[pid].js</code>:<syntaxhighlight lang="javascript"> | ||
import { useRouter } from 'next/router' | import { useRouter } from 'next/router' | ||
سطر 128: | سطر 122: | ||
export default Post | export default Post | ||
</syntaxhighlight>تجري مطابقة أي وجهة مثل <code>post/1/</code> أو<code>post/abc/</code> وغيرها مع المسار <code>pages/post/[pid].js</code> وسيُرسل معامل المسار على شكل استعلام إلى الصفحة وسيُدمج مع بقية معاملات الاستعلام. | </syntaxhighlight>تجري مطابقة أي وجهة مثل <code>post/1/</code> أو <code>post/abc/</code> وغيرها مع المسار <code>pages/post/[pid].js</code> وسيُرسل معامل المسار على شكل معامل استعلام query parameter إلى الصفحة وسيُدمج مع بقية معاملات الاستعلام. | ||
تمتلك الوجهة <code>post/abc/</code> مثلًا كائن الاستعلام <code>query</code> التالي:<syntaxhighlight lang="json"> | |||
{ "pid": "abc" } | { "pid": "abc" } | ||
</syntaxhighlight>كما | </syntaxhighlight>كما تمتلك الوجهة <code>post/abc?foo=bar/</code> أيضًا كائن الاستعلام <code>query</code> التالي:<syntaxhighlight lang="json"> | ||
{ "foo": "bar", "pid": "abc" } | { "foo": "bar", "pid": "abc" } | ||
</syntaxhighlight>لكن | </syntaxhighlight>لكن معاملات الوجهة ستلغي معاملات الاستعلام التي تتفق معها بالاسم. إذ تمتلك الوجهة <code>post/abc?pid=123/</code> مثلًا كائن الاستعلام <code>query</code> التالي:<syntaxhighlight lang="json"> | ||
{ "pid": "abc" } | { "pid": "abc" } | ||
</syntaxhighlight>تعمل الوجهات التي تضم عدة أجزاء ديناميكية بنفس الطريقة، إذ | </syntaxhighlight>تعمل الوجهات التي تضم عدة أجزاء ديناميكية بنفس الطريقة، إذ تطابق الصفحة <code>pages/post/[pid]/[comment].js</code> الوجهة <code>post/abc/a-comment/</code> وسيكون كائن الاستعلام <code>query</code> الخاص بها هو:<syntaxhighlight lang="json"> | ||
{ "pid": "abc", "comment": "a-comment" } | { "pid": "abc", "comment": "a-comment" } | ||
</syntaxhighlight>تُعالح المسارات الديناميكية للتنقل بين الصفحات في جانب العميل بواسطة المكوِّن [[Next.js/next link|<code>next/link</code>]]. فإن أردنا | </syntaxhighlight>تُعالح المسارات الديناميكية للتنقل بين الصفحات في جانب العميل بواسطة المكوِّن [[Next.js/next link|<code>next/link</code>]]. فإن أردنا إضافة روابط إلى الوجهات التي ذكرناها سابقًا، ستبدو كالتالي:<syntaxhighlight lang="javascript"> | ||
import Link from 'next/link' | import Link from 'next/link' | ||
سطر 145: | سطر 139: | ||
<ul> | <ul> | ||
<li> | <li> | ||
<Link href="/post/abc" | <Link href="/post/abc">Go to pages/post/[pid].js</Link> | ||
</li> | </li> | ||
<li> | <li> | ||
<Link href="/post/abc?foo=bar" | <Link href="/post/abc?foo=bar">Also goes to pages/post/[pid].js</Link> | ||
</li> | </li> | ||
<li> | <li> | ||
<Link href="/post/abc/a-comment"> | <Link href="/post/abc/a-comment"> | ||
Go to pages/post/[pid]/[comment].js | |||
</Link> | </Link> | ||
</li> | </li> | ||
سطر 164: | سطر 154: | ||
export default Home | export default Home | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== التقاط جميع الوجهات === | === التقاط جميع الوجهات === | ||
بالإمكان توسعة الوجهات الديناميكية لمطابقة كل المسارات وذلك بإضافة ثلاث نقاط | بالإمكان توسعة الوجهات الديناميكية لمطابقة كل المسارات وذلك بإضافة ثلاث نقاط <code>...</code> داخل القوسين المربعين. إليك مثالًا: | ||
* تطابق الوجهة <code>pages/post/[...slug].js</code> المسار <code>post/a/</code> كما تطابق <code>post/a/b/</code> و <code>post/a/b/c/</code> وهكذا. بإمكانك طبعًا اختيار أي اسم بدلًا من <code>slug</code> مثل <code>[param...]</code>. | * تطابق الوجهة <code>pages/post/[...slug].js</code> المسار <code>post/a/</code> كما تطابق <code>post/a/b/</code> و <code>post/a/b/c/</code> وهكذا. بإمكانك طبعًا اختيار أي اسم بدلًا من <code>slug</code> مثل <code>[param...]</code>. | ||
سطر 173: | سطر 164: | ||
تُرسل معاملات البحث كمعاملات استعلام ( <code>slug</code> مثال على ذلك) إلى الصفحة، وتشكل المعاملات دائمًا مصفوفة. وبالتالي سيكون كائن الاستعلام عن المسار <code>post/a/</code> مثلًا هو:<syntaxhighlight lang="json"> | تُرسل معاملات البحث كمعاملات استعلام ( <code>slug</code> مثال على ذلك) إلى الصفحة، وتشكل المعاملات دائمًا مصفوفة. وبالتالي سيكون كائن الاستعلام عن المسار <code>post/a/</code> مثلًا هو:<syntaxhighlight lang="json"> | ||
{ "slug": ["a"] } | { "slug": ["a"] } | ||
</syntaxhighlight>في حالة <code>post/a/b/</code> وأية مسارات مطابقة أخرى، ستُضاف | </syntaxhighlight>في حالة <code>post/a/b/</code> وأية مسارات مطابقة أخرى، ستُضاف المعاملات الجديدة إلى المصفوفة كالتالي:<syntaxhighlight lang="json"> | ||
{ "slug": ["a", "b"] } | { "slug": ["a", "b"] } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== التقاط جميع الوجهات | === التقاط جميع الوجهات اختياريًا === | ||
يمكن التقاط جميع الوجهات اختياريًا بإضافة المعامل بين قوسين مربعين مزدوجين مثل (<code><nowiki>[[...slug]]</nowiki></code>). إذ ستطابق الوجهة <code>pages/post/<nowiki>[[...slug]]</nowiki>.js</code> مثلًا المسارات <code>post/</code> و <code>post/a/</code> و <code>post/a/b/</code> وهكذا. | يمكن التقاط جميع الوجهات اختياريًا بإضافة المعامل بين قوسين مربعين مزدوجين مثل (<code><nowiki>[[...slug]]</nowiki></code>). إذ ستطابق الوجهة <code>pages/post/<nowiki>[[...slug]]</nowiki>.js</code> مثلًا المسارات <code>post/</code> و <code>post/a/</code> و <code>post/a/b/</code> وهكذا. | ||
إن الاختلاف الرئيسي بين التقاط جميع الوجهات والتقاط جميع الوجهات اختياريًا هو أن الالتقاط الاختياري سيطابق أيضًا المسارات التي لا تضم معامل البحث (المسار <code>post/</code> مثالًا). | إن الاختلاف الرئيسي بين التقاط جميع الوجهات والتقاط جميع الوجهات اختياريًا هو أن الالتقاط الاختياري سيطابق أيضًا المسارات التي لا تضم معامل البحث (المسار <code>post/</code> مثالًا). | ||
سيكون كائن الاستعلام <code>query</code> التالي:<syntaxhighlight lang=" | سيكون كائن الاستعلام <code>query</code> التالي:<syntaxhighlight lang="javascript"> | ||
{ } //`/post` سيحصل على المسار | { } //`/post` سيحصل على المسار | ||
{ "slug": ["a"] } //(مصفوفة وحيد العنصر) `/post/a` سيحصل على المسار | { "slug": ["a"] } //(مصفوفة وحيد العنصر) `/post/a` سيحصل على المسار | ||
سطر 188: | سطر 179: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== | === محاذير === | ||
* للوجهات المحددة مسبقًا الأفضلية على الوجهات | * للوجهات المحددة مسبقًا الأفضلية على الوجهات الديناميكية والتي لها الأفضلية على جميع المسارات الملتقطة. إليك بعض الأمثلة: | ||
** <code>pages/post/create.js</code> يتطابق مع | **<code>pages/post/create.js</code> يتطابق مع <code>post/create/</code>. | ||
** <code>pages/post/[pid].js</code> يتطابق مع <code>post/1/</code> و <code>post/abc/</code> وليس مع <code>post/create/</code>. | ** <code>pages/post/[pid].js</code> يتطابق مع <code>post/1/</code> و <code>post/abc/</code> وليس مع <code>post/create/</code>. | ||
** <code>pages/post/[...slug].js</code> يتطابق مع <code>post/1/2/</code> و <code>post/a/b/c/</code> | ** <code>pages/post/[...slug].js</code> يتطابق مع <code>post/1/2/</code> و <code>post/a/b/c/</code> وليس مع <code>post/create/</code> و <code>post/abc/</code>. | ||
* ستُرطَّب الصفحات التي تُحسّن بشكل ساكن من خلال [[Next.js/automatic static optimization|المحسّن الساكن الآلي]] Automatic Static Optimization دون تزويدها بمعاملات وجهاتها، أي سيكون كائن الاستعلام <code>query</code> مصفوفة فارغة (<code>{}</code>). وبعد الترطيب ستدفع Next.js | * ستُرطَّب الصفحات التي تُحسّن بشكل ساكن من خلال [[Next.js/automatic static optimization|المحسّن الساكن الآلي]] Automatic Static Optimization دون تزويدها بمعاملات وجهاتها، أي سيكون كائن الاستعلام <code>query</code> مصفوفة فارغة (<code>{}</code>). وبعد الترطيب ستدفع Next.js نحو تحديث التطبيق لتزويد مصفوفة الاستعلام بقيم المعاملات. | ||
== | == التوجيه القسري == | ||
يغطي الكائن [[Next.js/next link|<code>next/link</code>]] كل احتياجاتك في التوجه إلى الموارد | يغطي الكائن [[Next.js/next link|<code>next/link</code>]] كل احتياجاتك في التوجه إلى الموارد والصفحات، لكن بإمكانك أيضًا التنقل بين الصفحات في جانب العميل دون الحاجة إليه (الق نظرة على [[Next.js/next router|توثيق الواجهة البرمجية المتعلق بالوجهات]]). | ||
يظهر المثال التالي كيفية التنقل بين الصفحات باستخدام <code>useRouter</code>:<syntaxhighlight lang="javascript"> | يظهر المثال التالي كيفية التنقل بين الصفحات باستخدام <code>useRouter</code>:<syntaxhighlight lang="javascript"> | ||
سطر 213: | سطر 204: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== | == التوجيه السطحي == | ||
يتيح لك التوجه السطحي Shallow routing تغيير عنوان URL دون تنفيذ طرق إحضار البيانات مرة أخرى بما فيها <code>getServerSideProps</code> و <code>getStaticProps</code> و <code>getInitialProps</code>. ستحصل على المسار الحديث ومصفوفة الاستعلام الحديثة من خلال الكائن | يتيح لك التوجه السطحي Shallow routing تغيير عنوان URL دون تنفيذ طرق إحضار البيانات مرة أخرى بما فيها <code>getServerSideProps</code> و <code>getStaticProps</code> و <code>getInitialProps</code>. ستحصل على المسار الحديث ومصفوفة الاستعلام الحديثة من خلال الكائن [[Next.js/next router|<code>router</code>]] (الذي يضيفه <code>useRouter</code> أو <code>withRouter</code>) دون أن يفقد المكوّن حالته. | ||
لتمكين التوجه السطحي، اضبط قيمة الخيار <code>shallow</code> على <code>true</code>. إليك مثالًا:<syntaxhighlight lang="javascript"> | لتمكين التوجه السطحي، اضبط قيمة الخيار <code>shallow</code> على <code>true</code>. إليك مثالًا:<syntaxhighlight lang="javascript"> | ||
سطر 235: | سطر 226: | ||
export default Page | export default Page | ||
</syntaxhighlight> | </syntaxhighlight>سيُحدَّث عنوان URL إلى <code>?counter=10/</code>، لكن لن تُستبدل الصفحة بل حالة الوجهة route فقط. | ||
يمكنك أن ترى أيضًا كيف يتغير عنوان URL بواسطة المكوّن <code>componentDidUpdate</code> كالتالي:<syntaxhighlight lang="javascript"> | يمكنك أن ترى أيضًا كيف يتغير عنوان URL بواسطة المكوّن <code>componentDidUpdate</code> كالتالي:<syntaxhighlight lang="javascript"> | ||
componentDidUpdate(prevProps) { | componentDidUpdate(prevProps) { | ||
const { pathname, query } = this.props.router | const { pathname, query } = this.props.router | ||
// | // تأكد من تغير الخاصية لتفادي الحلقات المفرغة | ||
if (query.counter !== prevProps.router.query.counter) { | if (query.counter !== prevProps.router.query.counter) { | ||
// أحضر البيانات وفق الاستعلام الجديد | // أحضر البيانات وفق الاستعلام الجديد | ||
سطر 247: | سطر 238: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== | === محاذير === | ||
يعمل | يعمل التوجيه السطحي مع عناوين URL التي تتغير في الصفحة الحالية. لنفترض مثلًا وجود صفحة أخرى تُدعى <code>pages/about.js</code> ومن ثم نفَّذت الأمر التالي:<syntaxhighlight lang="javascript"> | ||
router.push('/?counter=10', '/about?counter=10', { shallow: true }) | router.push('/?counter=10', '/about?counter=10', { shallow: true }) | ||
</syntaxhighlight>طالما أنها صفحة جديدة، فلن تُحمّل الصفحة الحالية بل | </syntaxhighlight>طالما أنها صفحة جديدة، فلن تُحمّل الصفحة الحالية بل تُحمّل الجديدة وتُنتظر البيانات حتى تأتي حتى لو أردنا وجهة سطحية. | ||
إن جرى استخدام التوجيه السطحي مع برمجيات وسيطة middleware فليس هنالك ما يضمن أن تتطابق الصفحة الجديدة مع الحالية (كما رأيت في المثال ما قبل السابق)، وذلك يعود عادة إلى أن البرمجيات الوسيطة تملك صلاحيات إعادة الكتابة آليًا ولا يمكن التأكد من طرف العمل من ذلك دون عملية إحضار البيانات التي يجري تجاهلها مع استعمال <code>shallow</code> لذا يجب معاملة التغييرات التي تحصل مع التوجيه السطحي على أنها تغييرات سطحية دومًا. | |||
== أمثلة == | |||
* [https://github.com/vercel/next.js/tree/canary/examples/dynamic-routing Dynamic Routing] | |||
* [https://github.com/vercel/next.js/tree/canary/examples/catch-all-routes Catch All Routes] | |||
* [https://github.com/vercel/next.js/tree/canary/examples/using-router Using Router] | |||
* [https://github.com/vercel/next.js/tree/canary/examples/with-shallow-routing Shallow Routing] | |||
== المصادر == | == المصادر == | ||
* صفحات Router من توثيق Next.js الرسمي. | * صفحات [https://nextjs.org/docs/routing Router] من توثيق Next.js الرسمي. | ||
[[تصنيف:Next.js|{{SUBPAGENAME}}]] | |||
[[تصنيف:Next.js Routing|{{SUBPAGENAME}}]] |
المراجعة الحالية بتاريخ 17:17، 3 يناير 2023
تمتلك Next.js موجهًا للمسارات router يعتمد على نظام ملفات مبني على مفهوم الصفحات، فعندما يُضاف ملف إلى مجلد باسم pages فسيُتاح مباشرة كوجهة (أو مسار) route، ويمكن استخدام الملفات ضمن هذا المجلد لتعريف مختلف الأنماط المعروفة بالنسبة للوجهات.
أنماط الوجهات
تدعم Next.js عددًا من أنماط الوجهات سنتعرف عليها تباعًا.
الوجهات الخاصة بالصفحة Index
سيحدد الموجّه تلقائيًا موقع الصفحات التي تُدعى index
بالنسبة إلى المجلد الجذري كما في الأمثلة التالية:
pages/index.js → /
pages/blog/index.js → /blog
الوجهات المتداخلة
يدعم الموجّه الملفات المتداخلة، فلو أنشأت هيكلية متداخلة لمجلد سيحدد الموجّه تلقائيًا موقع كل منها بنفس الطريقة السابقة:
pages/blog/first-post.js → /blog/first-post
pages/dashboard/settings/username.js → /dashboard/settings/username
الأقسام الديناميكية لوجهة
لمطابقة جزء ديناميكي من مسار بإمكانك استخدام صيغة الأقواس المربعة، وسيتيح لك ذلك مطابقة معاملات مسماة named parameters:
pages/blog/[slug].js → /blog/:slug (/blog/hello-world)
pages/[username]/settings.js → /:username/settings (/foo/settings)
pages/post/[...all].js → /post/* (/post/2020/id/title)
سنتعرف أكثر على الوجهات الديناميكة تاليًا في صفحة التوثيق هذه.
الربط ما بين الصفحات
يتح لك موجّه Next.js الانتقال بين الصفحات في جانب العميل بشكل مشابهٍ للتطبيقات وحيدة الصفحة SPA اختصارًا إلى single-page application. يُستخدم مكوّن React الذي يُدعى Link
في إنجاز هذا الأمر من جانب العميل:
import Link from 'next/link'
function Home() {
return (
<ul>
<li>
<Link href="/">Home</Link>
</li>
<li>
<Link href="/about">About Us</Link>
</li>
<li>
<Link href="/blog/hello-world">Blog Post</Link>
</li>
</ul>
)
}
export default Home
يستخدم المثال السابق روابط عدة يتعلق كل منها بمسار href
إلى صفحة معروفة:
`/` → `pages/index.js`
`/about` → `pages/about.js`
`/blog/hello-world` → `pages/blog/[slug].js`
إن كل مكوّن </ Link>
في نافذة العرض viewport (بشكلها الأساسي أو بعد تمرير محتويات الشاشة) سيُحضَر مسبقًا prefetched بشكل افتراضي (بما في ذلك البيانات المتعلقة به) للصفحات التي تعتمد التوليد الساكن Static Generation أما الصفحات التي تعتمد على التصيير من جانب الخادم server-rendered فستُجلب بياناتها فقط عند الضغط على الرابط فقط.
الارتباط بمسارات ديناميكية
يمكن استخدام إنشاء مسار بشكل ديناميكي، وتظهر الفائدة من ذلك عندما يتضمن عنوان المسار أجزاءً ديناميكة. فلو أردت أن تعرض على سبيل المثال مجموعة من المنشورات مع روابط لها مُرِّرت إلى المكوّن على شكل خاصية، فيمكن استعمال الشيفرة التالية مثلًا:
import Link from 'next/link'
function Posts({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${encodeURIComponent(post.slug)}`}>
{post.title}
</Link>
</li>
))}
</ul>
)
}
export default Posts
ملاحظة: استُخدم الكائن encodeURIComponent
في المثال السابق للمحافظة على توافق المسار مع ترميز utf-8.
import Link from 'next/link'
function Posts({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link
href={{
pathname: '/blog/[slug]',
query: { slug: post.slug },
}}
>
{post.title}
</Link>
</li>
))}
</ul>
)
}
export default Posts
بدلًا من استخدام طريقة إنشاء المسار السابقة، استخدمنا في الحالة الثانية كائن URL كقيمة للخاصية href
:
pathname
: هو اسم الصفحة الموجودة في المجلدpages
، وهيblog/[slug]/
في مثالنا.query
: هو كائن يحتوي على جزء ديناميكي، وهوslug
في حالتنا.
الوصول إلى كائن التوجيه router
للوصول إلى الكائن router
في مكوّن React، يمكنك استخدام useRouter
أو withRouter
علمًا أنه من الأفضل استخدام useRouter
عمومًا.
الوجهات الديناميكية Dynamic Routes
لا يُعد تحديد المسارات مسبقًا أمرًا كافيًا للتطبيقات المعقدة، لهذا تتيح لك Next.js إمكانية إضافة أقواس مربعة حول صفحة ([param]
) لإنشاء وجهة ديناميكة.
لنتأمل الصفحة التالية pages/post/[pid].js
:
import { useRouter } from 'next/router'
const Post = () => {
const router = useRouter()
const { pid } = router.query
return <p>Post: {pid}</p>
}
export default Post
تجري مطابقة أي وجهة مثل post/1/
أو post/abc/
وغيرها مع المسار pages/post/[pid].js
وسيُرسل معامل المسار على شكل معامل استعلام query parameter إلى الصفحة وسيُدمج مع بقية معاملات الاستعلام.
تمتلك الوجهة post/abc/
مثلًا كائن الاستعلام query
التالي:
{ "pid": "abc" }
كما تمتلك الوجهة post/abc?foo=bar/
أيضًا كائن الاستعلام query
التالي:
{ "foo": "bar", "pid": "abc" }
لكن معاملات الوجهة ستلغي معاملات الاستعلام التي تتفق معها بالاسم. إذ تمتلك الوجهة post/abc?pid=123/
مثلًا كائن الاستعلام query
التالي:
{ "pid": "abc" }
تعمل الوجهات التي تضم عدة أجزاء ديناميكية بنفس الطريقة، إذ تطابق الصفحة pages/post/[pid]/[comment].js
الوجهة post/abc/a-comment/
وسيكون كائن الاستعلام query
الخاص بها هو:
{ "pid": "abc", "comment": "a-comment" }
تُعالح المسارات الديناميكية للتنقل بين الصفحات في جانب العميل بواسطة المكوِّن next/link
. فإن أردنا إضافة روابط إلى الوجهات التي ذكرناها سابقًا، ستبدو كالتالي:
import Link from 'next/link'
function Home() {
return (
<ul>
<li>
<Link href="/post/abc">Go to pages/post/[pid].js</Link>
</li>
<li>
<Link href="/post/abc?foo=bar">Also goes to pages/post/[pid].js</Link>
</li>
<li>
<Link href="/post/abc/a-comment">
Go to pages/post/[pid]/[comment].js
</Link>
</li>
</ul>
)
}
export default Home
التقاط جميع الوجهات
بالإمكان توسعة الوجهات الديناميكية لمطابقة كل المسارات وذلك بإضافة ثلاث نقاط ...
داخل القوسين المربعين. إليك مثالًا:
- تطابق الوجهة
pages/post/[...slug].js
المسارpost/a/
كما تطابقpost/a/b/
وpost/a/b/c/
وهكذا. بإمكانك طبعًا اختيار أي اسم بدلًا منslug
مثل[param...]
.
تُرسل معاملات البحث كمعاملات استعلام ( slug
مثال على ذلك) إلى الصفحة، وتشكل المعاملات دائمًا مصفوفة. وبالتالي سيكون كائن الاستعلام عن المسار post/a/
مثلًا هو:
{ "slug": ["a"] }
في حالة post/a/b/
وأية مسارات مطابقة أخرى، ستُضاف المعاملات الجديدة إلى المصفوفة كالتالي:
{ "slug": ["a", "b"] }
التقاط جميع الوجهات اختياريًا
يمكن التقاط جميع الوجهات اختياريًا بإضافة المعامل بين قوسين مربعين مزدوجين مثل ([[...slug]]
). إذ ستطابق الوجهة pages/post/[[...slug]].js
مثلًا المسارات post/
و post/a/
و post/a/b/
وهكذا.
إن الاختلاف الرئيسي بين التقاط جميع الوجهات والتقاط جميع الوجهات اختياريًا هو أن الالتقاط الاختياري سيطابق أيضًا المسارات التي لا تضم معامل البحث (المسار post/
مثالًا).
سيكون كائن الاستعلام query
التالي:
{ } //`/post` سيحصل على المسار
{ "slug": ["a"] } //(مصفوفة وحيد العنصر) `/post/a` سيحصل على المسار
{ "slug": ["a", "b"] } //(مصفوفة متعددة العناصر) `/post/a/b` سيحصل على
محاذير
- للوجهات المحددة مسبقًا الأفضلية على الوجهات الديناميكية والتي لها الأفضلية على جميع المسارات الملتقطة. إليك بعض الأمثلة:
pages/post/create.js
يتطابق معpost/create/
.pages/post/[pid].js
يتطابق معpost/1/
وpost/abc/
وليس معpost/create/
.pages/post/[...slug].js
يتطابق معpost/1/2/
وpost/a/b/c/
وليس معpost/create/
وpost/abc/
.
- ستُرطَّب الصفحات التي تُحسّن بشكل ساكن من خلال المحسّن الساكن الآلي Automatic Static Optimization دون تزويدها بمعاملات وجهاتها، أي سيكون كائن الاستعلام
query
مصفوفة فارغة ({}
). وبعد الترطيب ستدفع Next.js نحو تحديث التطبيق لتزويد مصفوفة الاستعلام بقيم المعاملات.
التوجيه القسري
يغطي الكائن next/link
كل احتياجاتك في التوجه إلى الموارد والصفحات، لكن بإمكانك أيضًا التنقل بين الصفحات في جانب العميل دون الحاجة إليه (الق نظرة على توثيق الواجهة البرمجية المتعلق بالوجهات).
يظهر المثال التالي كيفية التنقل بين الصفحات باستخدام useRouter
:
import { useRouter } from 'next/router'
export default function ReadMore() {
const router = useRouter()
return (
<button onClick={() => router.push('/about')}>
Click here to read more
</button>
)
}
التوجيه السطحي
يتيح لك التوجه السطحي Shallow routing تغيير عنوان URL دون تنفيذ طرق إحضار البيانات مرة أخرى بما فيها getServerSideProps
و getStaticProps
و getInitialProps
. ستحصل على المسار الحديث ومصفوفة الاستعلام الحديثة من خلال الكائن router
(الذي يضيفه useRouter
أو withRouter
) دون أن يفقد المكوّن حالته.
لتمكين التوجه السطحي، اضبط قيمة الخيار shallow
على true
. إليك مثالًا:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// الحالي URL هو عنوان '/'
function Page() {
const router = useRouter()
useEffect(() => {
// تنقَّل دائمًا بعد التصيير الأول
router.push('/?counter=10', undefined, { shallow: true })
}, [])
useEffect(() => {
// !تغيّر العداد
}, [router.query.counter])
}
export default Page
سيُحدَّث عنوان URL إلى ?counter=10/
، لكن لن تُستبدل الصفحة بل حالة الوجهة route فقط.
يمكنك أن ترى أيضًا كيف يتغير عنوان URL بواسطة المكوّن componentDidUpdate
كالتالي:
componentDidUpdate(prevProps) {
const { pathname, query } = this.props.router
// تأكد من تغير الخاصية لتفادي الحلقات المفرغة
if (query.counter !== prevProps.router.query.counter) {
// أحضر البيانات وفق الاستعلام الجديد
}
}
محاذير
يعمل التوجيه السطحي مع عناوين URL التي تتغير في الصفحة الحالية. لنفترض مثلًا وجود صفحة أخرى تُدعى pages/about.js
ومن ثم نفَّذت الأمر التالي:
router.push('/?counter=10', '/about?counter=10', { shallow: true })
طالما أنها صفحة جديدة، فلن تُحمّل الصفحة الحالية بل تُحمّل الجديدة وتُنتظر البيانات حتى تأتي حتى لو أردنا وجهة سطحية.
إن جرى استخدام التوجيه السطحي مع برمجيات وسيطة middleware فليس هنالك ما يضمن أن تتطابق الصفحة الجديدة مع الحالية (كما رأيت في المثال ما قبل السابق)، وذلك يعود عادة إلى أن البرمجيات الوسيطة تملك صلاحيات إعادة الكتابة آليًا ولا يمكن التأكد من طرف العمل من ذلك دون عملية إحضار البيانات التي يجري تجاهلها مع استعمال shallow
لذا يجب معاملة التغييرات التي تحصل مع التوجيه السطحي على أنها تغييرات سطحية دومًا.
أمثلة
المصادر
- صفحات Router من توثيق Next.js الرسمي.