الفرق بين المراجعتين لصفحة: «Next.js/next.config.js»
لا ملخص تعديل |
لا ملخص تعديل |
||
سطر 121: | سطر 121: | ||
export default Home | export default Home | ||
</syntaxhighlight> | |||
== إعادة الكتابة في Next.js == | |||
تتيح لك هملية إعادة الكتابة Rewrites ربط مسار طلب وارد إلى مسار وجهة أخرى. تعمل إعادة الكتابة مثل وسيط لعناوين URL وتقنِّع المسار إلى الوجهة ليظهر المستخدم وكأنه لم يغير مكانه في الموقع. بالمقابل تعيد عملية إعادة التوجيه redirects توجيه المستخدم إلى صفحة جديدة وتُظهر التغييرات على العنوان. | |||
ولتستخدم إعادة الكتابة أضف المفتاح <code>rewrites</code> إلى ملف التهيئة <code>next.config.js</code>:<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
async rewrites() { | |||
return [ | |||
{ | |||
source: '/about', | |||
destination: '/', | |||
}, | |||
] | |||
}, | |||
} | |||
</syntaxhighlight>تُطبق إعادة الكتابة عن التوجّه في جانب العميل، وتطبق إعادة الكتابة في المثال السابق مثلًا على العنصر <code><"Link href="/about></code>. | |||
إن الدالة هي دالة غير متزامنة تتوقع غعادة مصفوفة تضم كائنات تمتلك الخاصيتين <code>source</code> و <code>destination</code>: | |||
* <code>source</code>: من النوع <code>String</code>، وهو نموذج مسار الطلب الوارد. | |||
* <code>destination</code>: من النوع <code>String</code>، وهو المسار الذي تريد التوجه إليه. | |||
* <code>basePath</code>: تأخذ أحد القيمتين <code>false</code> أو <code>undefined</code>، فإن كانت القيمة <code>false</code> ، لن يضاف المسار الأساسي إلى العنوان عند المطابقة، ويستخد في إعادة الكتابة الخارجية. | |||
* <code>locale</code>: تأخذ أحد القيمتين <code>false</code> أو <code>undefined</code>، يحدد إن كان سُضاف الإعداد المحلي إلى المسار عند المطابقة. | |||
* <code>has</code>: مصفوفة من الكائنات <code>has objects</code> لها الخاصيات <code>type</code> و <code>key</code> و <code>value</code>. | |||
تجري عملية إعادة الكتابة بعد التحقق من منظومة الملفات (ملفات المجلدين <code>pages</code> و <code>public</code>) وقبل التوجه الديناميكي افتراضيًا. يمكن تغيير هذا السلوك بإعادة كائن بدلًا من المصفوفة من الدالة <code>rewrites</code> ابتداءً من النسخة <code>v10.1</code> من Next.js:<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
async rewrites() { | |||
return { | |||
beforeFiles: [ | |||
//يجري التحقق من عمليات إعادة الكتابة هذه بعد الترويسات | |||
// أو إعادة التوجيه | |||
//_next/public وقبل كل الملفات بما فيها ملفات المجلد | |||
// مما يسمح بتغيير ملفات الصفحة | |||
{ | |||
source: '/some-page', | |||
destination: '/somewhere-else', | |||
has: [{ type: 'query', key: 'overrideMe' }], | |||
}, | |||
], | |||
afterFiles: [ | |||
// pages/public يجري التحقق من عمليات إعادة الكتابة هذه بعد ملفات | |||
// لكن قبل الوجهات الديناميكية | |||
{ | |||
source: '/non-existent', | |||
destination: '/somewhere-else', | |||
}, | |||
], | |||
fallback: [ | |||
// pages/public يجري التحقق من عمليات إعادة الكتابة هذه بعد ملفات | |||
// وقبل الوجهات الديناميكية | |||
{ | |||
source: '/:path*', | |||
destination: `https://my-old-site.com/:path*`, | |||
}, | |||
], | |||
} | |||
}, | |||
} | |||
</syntaxhighlight>'''ملاحظة''': إن إجراء إعادة كتابة ضمن <code>beforeFiles</code> لا يتضمن التحقق المباشر من منظومة الملفات والوجهات الديناميكية بعد مطابقة المصدر، بل تستمر حتى يجري التحقق من جميع الإجراءات ضمن <code>beforeFiles</code>. إليك ترتيب التحقق من وجهات Next.js: | |||
# يجري التحقق من الترويسات headers أو تطبيقها. | |||
# يجري التحقق من عمليات إعادة التوجيه redirects أو تطبيقها. | |||
# يجري التحقق من عمليات إعادة الكتابة ضمن <code>beforeFiles</code> أو تطبيقها. | |||
# يجري التحقق من الملفات الساكنة في المجلد <code>public</code> و <code>next/static_</code> والصفحات غير الديناميكية أو تخديمها. | |||
# يجري التحقق من عمليات إعادة الكتابة ضمن <code>afterFiles</code> أو تطبيقها، فإن تطابقت إحدى عمليات إعادة الكتابة، نتحقق من الوجهة الديناميكية أو الملفات الساكنة بعد كل تطابق. | |||
# يجري التحقق من إعادة الكتابة ضمن <code>fallback</code> أو تطبيقها، ويجري تطبيقها قبل تصيير الصفحة 404، وبعد التحقق من الوجهات الديناميكية وجميع الموجودات الساكنة. | |||
=== معاملات الدالة <code>rewrites</code> === | |||
عند استخدام المعاملات، تُمرر هذه المعاملات إلى الدالة عبر الاستعلام افتراضيًا إن لم يُستخدم أيًا منها في الخاصية <code>destination</code>:<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
async rewrites() { | |||
return [ | |||
{ | |||
source: '/old-about/:path*', | |||
destination: '/about', //وبالتالي سيُمرر path لم يُستخدم المعامل | |||
// تلقائيًا ضمن الاستعلام | |||
}, | |||
] | |||
}, | |||
} | |||
</syntaxhighlight>أما إن استُخدم معامل في الوجهة <code>destination</code>، فلن يُمرر أي من المعاملات تلقائيًا ضمن الاستعلام:<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
async rewrites() { | |||
return [ | |||
{ | |||
source: '/docs/:path*', | |||
destination: '/:path*',//وبالتالي لن يُمرر path استُخدم المعامل | |||
// أي معامل تلقائيًا من خلال الاستعلام | |||
}, | |||
] | |||
}, | |||
} | |||
</syntaxhighlight>لكن لا يزال بإمكانك تمرير المعاملات يدويًا ضمن الاستعلام إن استُخدم أحدها في الخاصية <code>destination</code>:<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
async rewrites() { | |||
return [ | |||
{ | |||
source: '/:first/:second', | |||
destination: '/:first?second=:second', | |||
//قد استُخدم في الوجهة فلن يُضاف first طالما أن المعامل | |||
// تلقائيًا second المعامل الثاني | |||
// إلى الاستعلام على الرغم من إمكانية إضافته يدويًا كما في الأعلى | |||
}, | |||
] | |||
}, | |||
} | |||
</syntaxhighlight>'''ملاحظة''': تُحلَّل معاملات إعادة الكتابة للصفحات الساكنة الناتجة عن [[Next.js/automatic static optimization|التحسين التقائي الساكن]] أو [[Next.js/data fetching|التصيير المسبق]] من جانب العميل بعد الترطيب ويُزوَّد به الاستعلام. | |||
=== مطابقة المسارات === | |||
يُسمح بمطابقة المسارات كأن يُطابق المسار <code>blog/:slug/</code> المسار <code>blog/hello-world/</code> (دون تداخل في المسارات).<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
async rewrites() { | |||
return [ | |||
{ | |||
source: '/blog/:slug', | |||
destination: '/news/:slug', // يمكن مطابقة المعاملات هنا | |||
}, | |||
] | |||
}, | |||
} | |||
</syntaxhighlight> | |||
==== مطابقة المسارات بوجود محارف بديلة ==== | |||
لمطابقة مسار باستخدام محارف بديلة، بإمكانك استعمال <code>*</code> بعد المعامل، إذ سيُطابق المسار <code>/blog/:slug*</code> مثلًا المسار <code>/blog/a/b/c/d/hello-world</code>:<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
async rewrites() { | |||
return [ | |||
{ | |||
source: '/blog/:slug*', | |||
destination: '/news/:slug*', // يمكن مطابقة المعاملات هنا | |||
}, | |||
] | |||
}, | |||
} | |||
</syntaxhighlight> | |||
==== مطابقة المسارات من خلال التعابير النمطية Regex ==== | |||
لمطابقة مسار من خلال التعابير النمطية، غلِّف التعبير النمطي ضمن قوسين بعد المعامل. سيطابق المسار <code>blog/:slug(\\d{1,})/</code> مثلًا المسار <code>blog/123/</code> وليس المسار <code>blog/abc/</code>:<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
async rewrites() { | |||
return [ | |||
{ | |||
source: '/old-blog/:post(\\d{1,})', | |||
destination: '/blog/:post', // يمكن مطابقة المعاملات هنا | |||
}, | |||
] | |||
}, | |||
} | |||
</syntaxhighlight>تُستخدم المحارف التالية <code>(</code> و <code>)</code> و <code>{</code> و <code>}</code> و <code>:</code> و <code>*</code> و <code>+</code> و <code>?</code> في صياغة التعابير النمطية المستخدمة لمطابقة المسارات، فإن استُخدمت في الخاصية <code>source</code> كقيم غير خاصة non-special لا بد من تجاوزها بإضافة الصيغة قبلها:<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
async rewrites() { | |||
return [ | |||
{ | |||
//`/english(default)/something` سيثطابق ذلك | |||
source: '/english\\(default\\)/:slug', | |||
destination: '/en-us/:slug', | |||
}, | |||
] | |||
}, | |||
} | |||
</syntaxhighlight> | |||
=== مطابقة الترويسات وملفات تعريف الارتباط والاستعلام === | |||
بالإمكان مطابقة عملية إعادة كتابة عندما تُطابِق قيم الترويسات أو ملف تعريف الارتباط أو الاستعلام قيمة الحقل <code>has</code> فقط. أي يجب أن تتطابق الخاصية <code>source</code> وجميع عناصر <code>has</code> حتى تُطبَّق إعادة الكتابة. تمتلك العناصر <code>has</code> الحقول التالية: | |||
* <code>type</code>: من النوع <code>String</code>، وسيكون إما <code>header</code> أو <code>cookie</code> أو <code>host</code> أو <code>query</code>. | |||
* <code>key</code>: من النوع <code>String</code>، مفتاح النوع المختار <code>type</code> الذي سيُطابق. | |||
* <code>value</code>: من النوع <code>String</code> أو <code>undefined</code>، القيمة التي سيجري التحقق منها، وإن كانت غير محددة ستُطابق أي قيمة. يمكن استخدام سلسلة نصية تشابه التعبير النمطي لالتقاط جزء محدد من القيمة. فإن استُخدمت القيمة <code>first-(?<paramName>.*)</code> لمطابقة <code>first-second</code>، ستتمكن حينها من استخدام <code>second</code> في الوجهة مع المعامل <code>:paramName</code>. | |||
<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
async rewrites() { | |||
return [ | |||
// `x-rewrite-me` إن وجدت الترويسة | |||
// تُطبق إعادة الكتابة | |||
{ | |||
source: '/:path*', | |||
has: [ | |||
{ | |||
type: 'header', | |||
key: 'x-rewrite-me', | |||
}, | |||
], | |||
destination: '/another-page', | |||
}, | |||
// إن تطابقت الترويسة أو ملف الارتباط أو الاستعلام | |||
// تُطبق إعادة الكتابة | |||
{ | |||
source: '/specific/:path*', | |||
has: [ | |||
{ | |||
type: 'query', | |||
key: 'page', | |||
//لن تكون قيمة الصفحة متاحة في الوجهة لأن القيمة موجودة | |||
//(?<page>home) ولا تُستخدم مجموعة التقاط مُسماة مثل | |||
value: 'home', | |||
}, | |||
{ | |||
type: 'cookie', | |||
key: 'authorized', | |||
value: 'true', | |||
}, | |||
], | |||
destination: '/:path*/home', | |||
}, | |||
//وتحتوي على قيمة التطابق `x-authorized` إن وجدت الترويسة | |||
//ستُطيّق إعادة الكتابة | |||
{ | |||
source: '/:path*', | |||
has: [ | |||
{ | |||
type: 'header', | |||
key: 'x-authorized', | |||
value: '(?<authorized>yes|true)', | |||
}, | |||
], | |||
destination: '/home?authorized=:authorized', | |||
}, | |||
//ستُطبّق إعادة الكتابة `example.com` إن كان المضيف | |||
{ | |||
source: '/:path*', | |||
has: [ | |||
{ | |||
type: 'host', | |||
value: 'example.com', | |||
}, | |||
], | |||
destination: '/another-page', | |||
}, | |||
] | |||
}, | |||
} | |||
</syntaxhighlight> | |||
=== إعادة الكتابة إلى عنوان URL خارجي === | |||
بإمكانك إعادة الكتابة إلى عنوان URL خارجي، وهذا مفيد لتبني Next.js تدريجيًا. إليك مثالًا عن إعادة كتابة لتحويل الوجهة <code>blog/</code> لتطبيقك الرئيسي إلى موقع خارجي:<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
async rewrites() { | |||
return [ | |||
{ | |||
source: '/blog', | |||
destination: 'https://example.com/blog', | |||
}, | |||
{ | |||
source: '/blog/:slug', | |||
destination: 'https://example.com/blog/:slug', | |||
}, | |||
] | |||
}, | |||
} | |||
</syntaxhighlight>إن كنت تستخدم الحقل <code>trailingSlash: true</code> فستحتاج أيضًا إلى حشر محرف <code>/</code> في نهاية قيمة المعامل <code>source</code>. وإن كان الخادم الهدف يتوقع وجود المحرف <code>/</code> في نهاية القيمة، فلا بد من وضعها في المعامل <code>destination</code> أيضًا.<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
trailingSlash: true, | |||
async rewrites() { | |||
return [ | |||
{ | |||
source: '/blog/', | |||
destination: 'https://example.com/blog/', | |||
}, | |||
{ | |||
source: '/blog/:path*/', | |||
destination: 'https://example.com/blog/:path*/', | |||
}, | |||
] | |||
}, | |||
} | |||
</syntaxhighlight> | |||
==== تبني Next.js تدريجيًا ==== | |||
يمكنك دفع Next.js للتراجع كي تلعب دور الوسيط لموقع ويب موجود بعد التحقق من كل وجهات Next.js. لا حاجة هكذا لتغيير إعدادات إعادة الكتابة عند نقل صفحات أكثر إلى Next.js.<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
async rewrites() { | |||
return { | |||
fallback: [ | |||
{ | |||
source: '/:path*', | |||
destination: `https://custom-routes-proxying-endpoint.vercel.app/:path*`, | |||
}, | |||
], | |||
} | |||
}, | |||
} | |||
</syntaxhighlight>لمزيد من المعلومات راجع الصفحة "الانتقال إلى Next.js" من هذا [[Next.js/migrating|التوثيق]]. | |||
==== إعادة الكتابة مع دعم للمسار الأساسي ==== | |||
عند استخدام <code>basePath</code> مع إعادة الكتابة ستُضاف تلقائيًا البادئة إلى المعاملين <code>source</code> و <code>destination</code> ما لم تضف الإعداد <code>basePath: false</code> إلى دالة إعادة الكتابة:<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
basePath: '/docs', | |||
async rewrites() { | |||
return [ | |||
{ | |||
source: '/with-basePath', // /docs/with-basePath يصبج تلقائيًا | |||
destination: '/another', // /docs/another يصبح تلقائيًا | |||
}, | |||
{ | |||
//basePath: false لأن /without-basePath إلى /docs لا يضيف | |||
// لا يمكن استخدام ذلك في عمليات إعادة الكتابة الداخلية | |||
// destination: '/another' مثال | |||
source: '/without-basePath', | |||
destination: 'https://example.com', | |||
basePath: false, | |||
}, | |||
] | |||
}, | |||
} | |||
</syntaxhighlight> | |||
==== إعادة الكتابة مع دعم التوجه i18n ==== | |||
عند استخدام <code>i18n</code> مع إعادة الكتابة ستُضاف تلقائيًا البادئة المهيأة مسبقًا <code>locales</code> إلى المعاملين <code>source</code> و <code>destination</code> ما لم تضف الإعداد <code>locale: false</code> إلى دالة إعادة الكتابة، وفي حال استخدامه لا بد من إضافة تلك البادئة يدويًا حتى تُطابق بشكل صحيح:<syntaxhighlight lang="javascript"> | |||
module.exports = { | |||
i18n: { | |||
locales: ['en', 'fr', 'de'], | |||
defaultLocale: 'en', | |||
}, | |||
async rewrites() { | |||
return [ | |||
{ | |||
source: '/with-locale', // يتعامل مع الإعداد المحلي تلقائيًا | |||
destination: '/another', // يمرر الإعداد المحلي تلقائيًا | |||
}, | |||
{ | |||
// لا يُعالج الإعداد المحلي تلقائيًا | |||
source: '/nl/with-locale-manual', | |||
destination: '/nl/another', | |||
locale: false, | |||
}, | |||
{ | |||
// '/' يطابق | |||
// `en` لأن الإعداد المحلي الافتراضي | |||
source: '/en', | |||
destination: '/en/another', | |||
locale: false, | |||
}, | |||
{ | |||
// locale: false بإمكانك مطابقة جميع الإعدادات المحلية عندما يكون | |||
source: '/:locale/api-alias/:path*', | |||
destination: '/api/:path*', | |||
locale: false, | |||
}, | |||
{ | |||
// /(en|fr|de)/(.*) سيحول هذا إلى | |||
// لذا لن يُطابق المستوى الأعلى | |||
// `/` أو `/fr` | |||
source: '/(.*)', | |||
destination: '/another', | |||
}, | |||
] | |||
}, | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
مراجعة 18:16، 11 يوليو 2022
بإمكانك إنشاء الملف next.config.js
أو next.config.mjs
في جذر مشروعك وإلى جوار الملف package.json
، إن كنت ترغب في تطبيق إعدادت متقدمة على تطبيق Next.js.
يُعد الملف next.config.js
وحدة نمطية Node.js وليس ملف JSON، ويُستخدم من قبل خادم Next.js وخلال مراحل بناء التطبيق ولا يُضمَّن في تجميعة المتصفح.
إليك مثالًا عن ملف التهيئة next.config.js
:
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
/* ضع الإعدادات هنا */
}
module.exports = nextConfig
إن احتجت إلى وحدات ECMAScript بإمكانك استخدام الملف:
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
/* ضع الإعدادات هنا */
}
export default nextConfig
بإمكانك استخدام دالة أيضًا:
module.exports = (phase, { defaultConfig }) => {
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
/* ضع الإعدادات هنا */
}
return nextConfig
}
يمكن ابتداءً بالنسخة 12.1.0 استخدام دالة متزامنة:
module.exports = async (phase, { defaultConfig }) => {
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
/* ضع الإعدادات هنا */
}
return nextConfig
}
يُعد السياق phase
هو السياق الحالي لتحميل الإعدادات وتواجد سياقات أخرى أيضًا. يمكن إدراج المراحل phases من الوحدة next/constants
:
const { PHASE_DEVELOPMENT_SERVER } = require('next/constants')
module.exports = (phase, { defaultConfig }) => {
if (phase === PHASE_DEVELOPMENT_SERVER) {
return {
/* ضع إعدادات مرحلة التطوير هنا */
}
}
return {
/* ضع إعدادات كل المراحل هنا ما عدا مرحلة التطوير */
}
}
تُوضع الإعدادات المسموحة) في next.config.js
مكان أسطر التعليقات في الشيفرات السابقة. مع ذلك، لا حاجة إلى أية إعدادات، وليس من الضرورة فهم عمل كل إعداد. كل ما عليك هو البحث عن الميزات التي تريد تفعيلها أو تعديلها في هذه الصفحة وستريك ما العمل.
تفادى استخدام ميزات JavaScript الجديدة في اصدار Node.js الذي تستهدفه. فلن يُحلل الملف
next.config.js
من قبل Webpack أو Babel أو TypeScript.
متغيرات البيئة
تقدم ابتداءً من الإصدار تجربة تعليمية وعملية في إضافة متغيرات البيئة. حاول أن تجربها.
لإضافة متغيرات بيئة إلى تجميعة JavaScript، افتح الملف next.config.js
وأضف الإعداد env
:
module.exports = {
env: {
customKey: 'my-value',
},
}
بإمكانك الآن الوصول إلى process.env.customKey
في شيفرتك. إليك مثالًا:
function Page() {
return <h1>The value of customKey is: {process.env.customKey}</h1>
}
export default Page
تستبدل Next.js أثناء البناء المفاتيح process.env.customKey
بالقيمة 'my-value'
. وانتبه إلى أنك لن تستطيع تفكيك متغيرات process.env
نظرًا لطبيعة الإضافة DefinePlugin في webpack. فلو ألقينا نظرة مثلًا على السطر البرمجي التالي:
return <h1>The value of customKey is: {process.env.customKey}</h1>
سينتهي الأمر على النحو:
return <h1>The value of customKey is: {'my-value'}</h1>
المسار الأساسي
بإمكانك استخدام الإعداد basePath
لنشر تطبيق Next.js ضمن مسار فرعي لنطاق، إذ يسمح لك هذا الإعداد بضبط بادئة للمسار في تطبيقك. ولكي تستخدم مثلًا المسار docs/
بدلًا من المسار الأساسي الافتراضي /
، افتح الملف next.config.js
وأضف الإعداد basePath
كالتالي:
module.exports = {
basePath: '/docs',
}
ملاحظة: لا بد من ضبط هذه القيمة أثناء البناء ولا يمكن تغييرها دون إعادة بناء التطبيق، لأن القيمة ستضاف مباشرة إلى تجميع الشيفرة الخاصة بجانب العميل.
الروابط
عند ربط الصفحة بغيرها باستخدام next/link
و next/router
سيُطبق الإعداد basePath
تلقائيًا. إذ سيتحول على سبيل المثال المسار about/
إلى docs/about/
تلقائيًا عند ضبط قيمة الإعداد basePath
على docs/
.
export default function HomePage() {
return (
<>
<Link href="/about">
<a>About Page</a>
</Link>
</>
)
}
وسيكون خرج HTML كالتالي:
<a href="/docs/about">About Page</a>
يضمن ذلك أنك لن تُضطر إلى تغيير كل الروابط في تطبيقك عند تغيير قيمة الإعداد basePath
.
الصور
لا بد من إضافة قيمة الإعداد basePath
قبل قيمة الخاصية src
إن كنت تريد استخدام المكوّن next/image
. فالمسار docs/me.png/
سيخدّم صورتك بالشكل الصحيح إن قررت أن تكون قيمة الإعداد basePath
هي docs/
.
import Image from 'next/image'
function Home() {
return (
<>
<h1>My Homepage</h1>
<Image
src="/docs/me.png"
alt="Picture of the author"
width={500}
height={500}
/>
<p>Welcome to my homepage!</p>
</>
)
}
export default Home
إعادة الكتابة في Next.js
تتيح لك هملية إعادة الكتابة Rewrites ربط مسار طلب وارد إلى مسار وجهة أخرى. تعمل إعادة الكتابة مثل وسيط لعناوين URL وتقنِّع المسار إلى الوجهة ليظهر المستخدم وكأنه لم يغير مكانه في الموقع. بالمقابل تعيد عملية إعادة التوجيه redirects توجيه المستخدم إلى صفحة جديدة وتُظهر التغييرات على العنوان.
ولتستخدم إعادة الكتابة أضف المفتاح rewrites
إلى ملف التهيئة next.config.js
:
module.exports = {
async rewrites() {
return [
{
source: '/about',
destination: '/',
},
]
},
}
تُطبق إعادة الكتابة عن التوجّه في جانب العميل، وتطبق إعادة الكتابة في المثال السابق مثلًا على العنصر <"Link href="/about>
.
إن الدالة هي دالة غير متزامنة تتوقع غعادة مصفوفة تضم كائنات تمتلك الخاصيتين source
و destination
:
source
: من النوعString
، وهو نموذج مسار الطلب الوارد.destination
: من النوعString
، وهو المسار الذي تريد التوجه إليه.basePath
: تأخذ أحد القيمتينfalse
أوundefined
، فإن كانت القيمةfalse
، لن يضاف المسار الأساسي إلى العنوان عند المطابقة، ويستخد في إعادة الكتابة الخارجية.locale
: تأخذ أحد القيمتينfalse
أوundefined
، يحدد إن كان سُضاف الإعداد المحلي إلى المسار عند المطابقة.has
: مصفوفة من الكائناتhas objects
لها الخاصياتtype
وkey
وvalue
.
تجري عملية إعادة الكتابة بعد التحقق من منظومة الملفات (ملفات المجلدين pages
و public
) وقبل التوجه الديناميكي افتراضيًا. يمكن تغيير هذا السلوك بإعادة كائن بدلًا من المصفوفة من الدالة rewrites
ابتداءً من النسخة v10.1
من Next.js:
module.exports = {
async rewrites() {
return {
beforeFiles: [
//يجري التحقق من عمليات إعادة الكتابة هذه بعد الترويسات
// أو إعادة التوجيه
//_next/public وقبل كل الملفات بما فيها ملفات المجلد
// مما يسمح بتغيير ملفات الصفحة
{
source: '/some-page',
destination: '/somewhere-else',
has: [{ type: 'query', key: 'overrideMe' }],
},
],
afterFiles: [
// pages/public يجري التحقق من عمليات إعادة الكتابة هذه بعد ملفات
// لكن قبل الوجهات الديناميكية
{
source: '/non-existent',
destination: '/somewhere-else',
},
],
fallback: [
// pages/public يجري التحقق من عمليات إعادة الكتابة هذه بعد ملفات
// وقبل الوجهات الديناميكية
{
source: '/:path*',
destination: `https://my-old-site.com/:path*`,
},
],
}
},
}
ملاحظة: إن إجراء إعادة كتابة ضمن beforeFiles
لا يتضمن التحقق المباشر من منظومة الملفات والوجهات الديناميكية بعد مطابقة المصدر، بل تستمر حتى يجري التحقق من جميع الإجراءات ضمن beforeFiles
. إليك ترتيب التحقق من وجهات Next.js:
- يجري التحقق من الترويسات headers أو تطبيقها.
- يجري التحقق من عمليات إعادة التوجيه redirects أو تطبيقها.
- يجري التحقق من عمليات إعادة الكتابة ضمن
beforeFiles
أو تطبيقها. - يجري التحقق من الملفات الساكنة في المجلد
public
وnext/static_
والصفحات غير الديناميكية أو تخديمها. - يجري التحقق من عمليات إعادة الكتابة ضمن
afterFiles
أو تطبيقها، فإن تطابقت إحدى عمليات إعادة الكتابة، نتحقق من الوجهة الديناميكية أو الملفات الساكنة بعد كل تطابق. - يجري التحقق من إعادة الكتابة ضمن
fallback
أو تطبيقها، ويجري تطبيقها قبل تصيير الصفحة 404، وبعد التحقق من الوجهات الديناميكية وجميع الموجودات الساكنة.
معاملات الدالة rewrites
عند استخدام المعاملات، تُمرر هذه المعاملات إلى الدالة عبر الاستعلام افتراضيًا إن لم يُستخدم أيًا منها في الخاصية destination
:
module.exports = {
async rewrites() {
return [
{
source: '/old-about/:path*',
destination: '/about', //وبالتالي سيُمرر path لم يُستخدم المعامل
// تلقائيًا ضمن الاستعلام
},
]
},
}
أما إن استُخدم معامل في الوجهة destination
، فلن يُمرر أي من المعاملات تلقائيًا ضمن الاستعلام:
module.exports = {
async rewrites() {
return [
{
source: '/docs/:path*',
destination: '/:path*',//وبالتالي لن يُمرر path استُخدم المعامل
// أي معامل تلقائيًا من خلال الاستعلام
},
]
},
}
لكن لا يزال بإمكانك تمرير المعاملات يدويًا ضمن الاستعلام إن استُخدم أحدها في الخاصية destination
:
module.exports = {
async rewrites() {
return [
{
source: '/:first/:second',
destination: '/:first?second=:second',
//قد استُخدم في الوجهة فلن يُضاف first طالما أن المعامل
// تلقائيًا second المعامل الثاني
// إلى الاستعلام على الرغم من إمكانية إضافته يدويًا كما في الأعلى
},
]
},
}
ملاحظة: تُحلَّل معاملات إعادة الكتابة للصفحات الساكنة الناتجة عن التحسين التقائي الساكن أو التصيير المسبق من جانب العميل بعد الترطيب ويُزوَّد به الاستعلام.
مطابقة المسارات
يُسمح بمطابقة المسارات كأن يُطابق المسار blog/:slug/
المسار blog/hello-world/
(دون تداخل في المسارات).
module.exports = {
async rewrites() {
return [
{
source: '/blog/:slug',
destination: '/news/:slug', // يمكن مطابقة المعاملات هنا
},
]
},
}
مطابقة المسارات بوجود محارف بديلة
لمطابقة مسار باستخدام محارف بديلة، بإمكانك استعمال *
بعد المعامل، إذ سيُطابق المسار /blog/:slug*
مثلًا المسار /blog/a/b/c/d/hello-world
:
module.exports = {
async rewrites() {
return [
{
source: '/blog/:slug*',
destination: '/news/:slug*', // يمكن مطابقة المعاملات هنا
},
]
},
}
مطابقة المسارات من خلال التعابير النمطية Regex
لمطابقة مسار من خلال التعابير النمطية، غلِّف التعبير النمطي ضمن قوسين بعد المعامل. سيطابق المسار blog/:slug(\\d{1,})/
مثلًا المسار blog/123/
وليس المسار blog/abc/
:
module.exports = {
async rewrites() {
return [
{
source: '/old-blog/:post(\\d{1,})',
destination: '/blog/:post', // يمكن مطابقة المعاملات هنا
},
]
},
}
تُستخدم المحارف التالية (
و )
و {
و }
و :
و *
و +
و ?
في صياغة التعابير النمطية المستخدمة لمطابقة المسارات، فإن استُخدمت في الخاصية source
كقيم غير خاصة non-special لا بد من تجاوزها بإضافة الصيغة قبلها:
module.exports = {
async rewrites() {
return [
{
//`/english(default)/something` سيثطابق ذلك
source: '/english\\(default\\)/:slug',
destination: '/en-us/:slug',
},
]
},
}
مطابقة الترويسات وملفات تعريف الارتباط والاستعلام
بالإمكان مطابقة عملية إعادة كتابة عندما تُطابِق قيم الترويسات أو ملف تعريف الارتباط أو الاستعلام قيمة الحقل has
فقط. أي يجب أن تتطابق الخاصية source
وجميع عناصر has
حتى تُطبَّق إعادة الكتابة. تمتلك العناصر has
الحقول التالية:
type
: من النوعString
، وسيكون إماheader
أوcookie
أوhost
أوquery
.key
: من النوعString
، مفتاح النوع المختارtype
الذي سيُطابق.value
: من النوعString
أوundefined
، القيمة التي سيجري التحقق منها، وإن كانت غير محددة ستُطابق أي قيمة. يمكن استخدام سلسلة نصية تشابه التعبير النمطي لالتقاط جزء محدد من القيمة. فإن استُخدمت القيمةfirst-(?<paramName>.*)
لمطابقةfirst-second
، ستتمكن حينها من استخدامsecond
في الوجهة مع المعامل:paramName
.
module.exports = {
async rewrites() {
return [
// `x-rewrite-me` إن وجدت الترويسة
// تُطبق إعادة الكتابة
{
source: '/:path*',
has: [
{
type: 'header',
key: 'x-rewrite-me',
},
],
destination: '/another-page',
},
// إن تطابقت الترويسة أو ملف الارتباط أو الاستعلام
// تُطبق إعادة الكتابة
{
source: '/specific/:path*',
has: [
{
type: 'query',
key: 'page',
//لن تكون قيمة الصفحة متاحة في الوجهة لأن القيمة موجودة
//(?<page>home) ولا تُستخدم مجموعة التقاط مُسماة مثل
value: 'home',
},
{
type: 'cookie',
key: 'authorized',
value: 'true',
},
],
destination: '/:path*/home',
},
//وتحتوي على قيمة التطابق `x-authorized` إن وجدت الترويسة
//ستُطيّق إعادة الكتابة
{
source: '/:path*',
has: [
{
type: 'header',
key: 'x-authorized',
value: '(?<authorized>yes|true)',
},
],
destination: '/home?authorized=:authorized',
},
//ستُطبّق إعادة الكتابة `example.com` إن كان المضيف
{
source: '/:path*',
has: [
{
type: 'host',
value: 'example.com',
},
],
destination: '/another-page',
},
]
},
}
إعادة الكتابة إلى عنوان URL خارجي
بإمكانك إعادة الكتابة إلى عنوان URL خارجي، وهذا مفيد لتبني Next.js تدريجيًا. إليك مثالًا عن إعادة كتابة لتحويل الوجهة blog/
لتطبيقك الرئيسي إلى موقع خارجي:
module.exports = {
async rewrites() {
return [
{
source: '/blog',
destination: 'https://example.com/blog',
},
{
source: '/blog/:slug',
destination: 'https://example.com/blog/:slug',
},
]
},
}
إن كنت تستخدم الحقل trailingSlash: true
فستحتاج أيضًا إلى حشر محرف /
في نهاية قيمة المعامل source
. وإن كان الخادم الهدف يتوقع وجود المحرف /
في نهاية القيمة، فلا بد من وضعها في المعامل destination
أيضًا.
module.exports = {
trailingSlash: true,
async rewrites() {
return [
{
source: '/blog/',
destination: 'https://example.com/blog/',
},
{
source: '/blog/:path*/',
destination: 'https://example.com/blog/:path*/',
},
]
},
}
تبني Next.js تدريجيًا
يمكنك دفع Next.js للتراجع كي تلعب دور الوسيط لموقع ويب موجود بعد التحقق من كل وجهات Next.js. لا حاجة هكذا لتغيير إعدادات إعادة الكتابة عند نقل صفحات أكثر إلى Next.js.
module.exports = {
async rewrites() {
return {
fallback: [
{
source: '/:path*',
destination: `https://custom-routes-proxying-endpoint.vercel.app/:path*`,
},
],
}
},
}
لمزيد من المعلومات راجع الصفحة "الانتقال إلى Next.js" من هذا التوثيق.
إعادة الكتابة مع دعم للمسار الأساسي
عند استخدام basePath
مع إعادة الكتابة ستُضاف تلقائيًا البادئة إلى المعاملين source
و destination
ما لم تضف الإعداد basePath: false
إلى دالة إعادة الكتابة:
module.exports = {
basePath: '/docs',
async rewrites() {
return [
{
source: '/with-basePath', // /docs/with-basePath يصبج تلقائيًا
destination: '/another', // /docs/another يصبح تلقائيًا
},
{
//basePath: false لأن /without-basePath إلى /docs لا يضيف
// لا يمكن استخدام ذلك في عمليات إعادة الكتابة الداخلية
// destination: '/another' مثال
source: '/without-basePath',
destination: 'https://example.com',
basePath: false,
},
]
},
}
إعادة الكتابة مع دعم التوجه i18n
عند استخدام i18n
مع إعادة الكتابة ستُضاف تلقائيًا البادئة المهيأة مسبقًا locales
إلى المعاملين source
و destination
ما لم تضف الإعداد locale: false
إلى دالة إعادة الكتابة، وفي حال استخدامه لا بد من إضافة تلك البادئة يدويًا حتى تُطابق بشكل صحيح:
module.exports = {
i18n: {
locales: ['en', 'fr', 'de'],
defaultLocale: 'en',
},
async rewrites() {
return [
{
source: '/with-locale', // يتعامل مع الإعداد المحلي تلقائيًا
destination: '/another', // يمرر الإعداد المحلي تلقائيًا
},
{
// لا يُعالج الإعداد المحلي تلقائيًا
source: '/nl/with-locale-manual',
destination: '/nl/another',
locale: false,
},
{
// '/' يطابق
// `en` لأن الإعداد المحلي الافتراضي
source: '/en',
destination: '/en/another',
locale: false,
},
{
// locale: false بإمكانك مطابقة جميع الإعدادات المحلية عندما يكون
source: '/:locale/api-alias/:path*',
destination: '/api/:path*',
locale: false,
},
{
// /(en|fr|de)/(.*) سيحول هذا إلى
// لذا لن يُطابق المستوى الأعلى
// `/` أو `/fr`
source: '/(.*)',
destination: '/another',
},
]
},
}
المصادر
- الصفحات Next.config.js من توثيق Next.js الرسمي.