التعامل مع ملف التهيئة next.config.js في Next.js
بإمكانك إنشاء الملف 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
ابتداءً من النسخة 10.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',
},
]
},
}
إعادة التوجيه
يسمح لك إعادة التوجيه أن تحوّل مسار الطلب الوارد إلى وجهة مختلفة. ولا ستخدام إعادة التوجيه، لا بد من إضافة المفتاح redirects
إلى ملف التهيئة next.config.js
:
module.exports = {
async redirects() {
return [
{
source: '/about',
destination: '/',
permanent: true,
},
]
},
}
تُعد الدالة redirects
دالة غير متزامنة تتوقع إعادة مصفوفة تضم كائنات تمتلك الخاصيات source
و destination
و permanent
:
source
: من النوعString
، وهو نموذج مسار الطلب الوارد.destination
: من النوعString
، وهو المسار الذي تريد التوجه إليه.permanent
: من النوع المنطقي، فإن كانت قيمتهاtrue
سيُستخدم رمز الحالة 308 الذي يملي على العميل أو محرك البحث تخزين إعادة التوجيه إلى ما لا نهاية. أما في حال كانfalse
سيُستخدم رمز الحالة 307 وهو مؤقت ولا حاجة لتخزينه.قد تلاحظ استخدام رمز الحالة
307
لإعادة التوجه المؤقت و308
لإعادة التوجه الدائم مع()redirect
. وعلى الرغم من استخدام الرمزين302
و301
على الترتيب تقليديًا، إلا أن العديد من المتصفحات قد غيرت نوع طلب إعادة التوجه من إلىPOST
إلىGET
عند استخدام الرمز302
بغض النظر عن أصول نوع الطلب. فلو أنشأ المتصفح طلبًا إلى`POST /v1/users
وأعاد رمز الحالة302
مع الموقعv2/users/
، فقد تكون الطلبات التالية من النوعGET /v2/users
بدلًا من النوع المتوقعPOST /v2/users
. وبالتالي استخدمت Next.js رمزي الحالة 308 و 307 لحفظ نوع الطلب صراحةbasePath
: تأخذ أحد القيمتينfalse
أوundefined
، فإن كانت القيمةfalse
، لن يضاف المسار الأساسي إلى العنوان عند المطابقة، ويستخد في إعادة الكتابة الخارجية.locale
: تأخذ أحد القيمتينfalse
أوundefined
، يحدد إن كان سُضاف الإعداد المحلي إلى المسار عند المطابقة.has
: مصفوفة من الكائناتhas objects
لها الخاصياتtype
وkey
وvalue
.
يجري التحقق من عمليات إعادة التوجه قبل التحقق من منظومة الملفات التي تضم ملفات المجلدين pages
و public
. عندما يُطبق إعادة التوجيه، ستُمرر أي قيم للاستعلام ضمن الطلب إلى الوجهة الجديدة. ألق نظرة على الإعدادات التالية:
{
source: '/old-blog/:path*',
destination: '/blog/:path*',
permanent: false
}
فعندما يكون الطلب من الشكل old-blog/post-1?hello=world/
مثلًا سيُعاد توجيه العميل إلى blog/post-1?hello=world/
.
مطابقة المسارات
يُسمح بمطابقة المسارات كأن يُطابق المسار blog/:slug/
المسار blog/hello-world/
(دون تداخل في المسارات).
module.exports = {
async redirects() {
return [
{
source: '/old-blog/:slug',
destination: '/news/:slug', // Matched parameters can be used in the destination
permanent: true,
},
]
},
}
مطابقة المسارات بوجود محارف بديلة
لمطابقة مسار باستخدام محارف بديلة، بإمكانك استعمال *
بعد المعامل، إذ سيُطابق المسار *blog/:slug/
مثلًا المسار blog/a/b/c/d/hello-world/
:
module.exports = {
async redirects() {
return [
{
source: '/blog/:slug*',
destination: '/news/:slug*', // Matched parameters can be used in the destination
permanent: true,
},
]
},
}
مطابقة المسارات من خلال التعابير النمطية Regex
لمطابقة مسار من خلال التعابير النمطية، غلِّف التعبير النمطي ضمن قوسين بعد المعامل. سيطابق المسار post/:slug(\\d{1,})/
مثلًا المسار post/123/
وليس المسار post/abc/
:
module.exports = {
async redirects() {
return [
{
source: '/post/:slug(\\d{1,})',
destination: '/news/:slug', // Matched parameters can be used in the destination
permanent: false,
},
]
},
}
تُستخدم المحارف التالية (
و )
و {
و }
و :
و *
و +
و ?
في صياغة التعابير النمطية المستخدمة لمطابقة المسارات، فإن استُخدمت في الخاصية source
كقيم غير خاصة non-special لا بد من تجاوزها بإضافة الصيغة \\
قبلها:
module.exports = {
async redirects() {
return [
{
// this will match `/english(default)/something` being requested
source: '/english\\(default\\)/:slug',
destination: '/en-us/:slug',
permanent: false,
},
]
},
}
مطابقة الترويسات وملفات تعريف الارتباط والاستعلام
بالإمكان مطابقة عملية إعادة كتابة عندما تُطابِق قيم الترويسات أو ملف تعريف الارتباط أو الاستعلام قيمة الحقل 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 redirects() {
return [
// if the header `x-redirect-me` is present,
// this redirect will be applied
{
source: '/:path((?!another-page$).*)',
has: [
{
type: 'header',
key: 'x-redirect-me',
},
],
permanent: false,
destination: '/another-page',
},
// if the source, query, and cookie are matched,
// this redirect will be applied
{
source: '/specific/:path*',
has: [
{
type: 'query',
key: 'page',
// the page value will not be available in the
// destination since value is provided and doesn't
// use a named capture group e.g. (?<page>home)
value: 'home',
},
{
type: 'cookie',
key: 'authorized',
value: 'true',
},
],
permanent: false,
destination: '/another/:path*',
},
// if the header `x-authorized` is present and
// contains a matching value, this redirect will be applied
{
source: '/',
has: [
{
type: 'header',
key: 'x-authorized',
value: '(?<authorized>yes|true)',
},
],
permanent: false,
destination: '/home?authorized=:authorized',
},
// if the host is `example.com`,
// this redirect will be applied
{
source: '/:path((?!another-page$).*)',
has: [
{
type: 'host',
value: 'example.com',
},
],
permanent: false,
destination: '/another-page',
},
]
},
}
إعادة التوجيه مع دعم للمسار الأساسي
عند استخدام basePath
مع إعادة التوجيه ستُضاف تلقائيًا البادئة إلى المعاملين source
و destination
ما لم تضف الإعداد basePath: false
إلى دالة إعادة التوجيه:
module.exports = {
basePath: '/docs',
async redirects() {
return [
{
source: '/with-basePath', // automatically becomes /docs/with-basePath
destination: '/another', // automatically becomes /docs/another
permanent: false,
},
{
// does not add /docs since basePath: false is set
source: '/without-basePath',
destination: '/another',
basePath: false,
permanent: false,
},
]
},
}
إعادة التوجيه مع دعم التوجه i18n
عند استخدام i18n
مع إعادة الكتابة ستُضاف تلقائيًا البادئة المهيأة مسبقًا locales
إلى المعاملين source
و destination
ما لم تضف الإعداد locale: false
إلى دالة إعادة التوجيه، وفي حال استخدامه لا بد من إضافة تلك البادئة يدويًا حتى تُطابق بشكل صحيح:
module.exports = {
i18n: {
locales: ['en', 'fr', 'de'],
defaultLocale: 'en',
},
async redirects() {
return [
{
source: '/with-locale', // automatically handles all locales
destination: '/another', // automatically passes the locale on
permanent: false,
},
{
// does not handle locales automatically since locale: false is set
source: '/nl/with-locale-manual',
destination: '/nl/another',
locale: false,
permanent: false,
},
{
// this matches '/' since `en` is the defaultLocale
source: '/en',
destination: '/en/another',
locale: false,
permanent: false,
},
// it's possible to match all locales even when locale: false is set
{
source: '/:locale/page',
destination: '/en/newpage',
permanent: false,
locale: false,
}
{
// this gets converted to /(en|fr|de)/(.*) so will not match the top-level
// `/` or `/fr` routes like /:path* would
source: '/(.*)',
destination: '/another',
permanent: false,
},
]
},
}
قد تحتاج في حالات نادرة تعيين رموز حالة مخصصة لعملاء HTTP
الأقدم كي يُعاد توجيههم بالشكل الصحيح. في حالات كهذه، استخدم الخاصية statusCode
بدلًا من permanent
لكن لا تستخدمهما معًا. تجدر الإشارة إلى إضافة الترويسة تلقائيًا إلى رمز الحالة 308 لضمان التوافق مع المتصفح IE11.
أنواع أخرى لإعادة التوجيه
- بإمكانك استخدام
()res.redirect
ضمن وجهات API. - يمكنك إعاة توجيه صفحات محددة عند الطلب ضمن
getStaticProps
وgetServerSideProps
.
الترويسات
تتيح لك إعداد ترويسات HTTP مخصصة عند الاستجابة إلى طلب وارد عبر مسار معين. ولإ عداد ترويسة مخصصة، استخدم المفتاح headers
ضمن الملف next.config.js
:
module.exports = {
async headers() {
return [
{
source: '/about',
headers: [
{
key: 'x-custom-header',
value: 'my custom header value',
},
{
key: 'x-another-custom-header',
value: 'my other custom header value',
},
],
},
]
},
}
تُعد الدالة headers
دالة غير متزامنة تتوقع إعادة مصفوفة تضم كائنات تمتلك الخاصيات source
و headers
:
source
: من النوعString
، وهو نموذج مسار الطلب الوارد.headers
: مصفوفة من كائنات ترويسة الاستجابة، ولها الخاصيتانkey
وvalue
.basePath
: تأخذ أحد القيمتينfalse
أوundefined
، فإن كانت القيمةfalse
، لن يضاف المسار الأساسي إلى العنوان عند المطابقة، ويستخد في إعادة الكتابة الخارجية.locale
: تأخذ أحد القيمتينfalse
أوundefined
، يحدد إن كان سُضاف الإعداد المحلي إلى المسار عند المطابقة.has
: مصفوفة من الكائناتhas objects
لها الخاصياتtype
وkey
وvalue
.
يجري التحقق من الترويسات قبل التحقق من منظومة الملفات التي تضم ملفات المجلدين pages
و public
.
سلوك الإلغاء في الترويسات
إن تطابقت ترويستان مع المسار ذاته واستهدفتا نفس المفتاح، سيُلغي مفتاح الترويسة الأخيرة مفتاح الأولى. في المثال التالي، سينتج المسار hello/
عن الترويسة x-hello
التي قيمتها world
لأنها الترويسة الأخيرة من ترويستين لهما نفس المفتاح x-hello
:
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'x-hello',
value: 'there',
},
],
},
{
source: '/hello',
headers: [
{
key: 'x-hello',
value: 'world',
},
],
},
]
},
}
مطابقة المسارات
يُسمح بمطابقة المسارات كأن يُطابق المسار blog/:slug/
المسار blog/hello-world/
(دون تداخل في المسارات).
module.exports = {
async headers() {
return [
{
source: '/blog/:slug',
headers: [
{
key: 'x-slug',
value: ':slug', // Matched parameters can be used in the value
},
{
key: 'x-slug-:slug', // Matched parameters can be used in the key
value: 'my other custom header value',
},
],
},
]
},
}
مطابقة المسارات بوجود محارف بديلة
لمطابقة مسار باستخدام محارف بديلة، بإمكانك استعمال *
بعد المعامل، إذ سيُطابق المسار *blog/:slug/
مثلًا المسار blog/a/b/c/d/hello-world/
:
module.exports = {
async headers() {
return [
{
source: '/blog/:slug*',
headers: [
{
key: 'x-slug',
value: ':slug*', // Matched parameters can be used in the value
},
{
key: 'x-slug-:slug*', // Matched parameters can be used in the key
value: 'my other custom header value',
},
],
},
]
},
}
مطابقة المسارات من خلال التعابير النمطية Regex
لمطابقة مسار من خلال التعابير النمطية، غلِّف التعبير النمطي ضمن قوسين بعد المعامل. سيطابق المسار blog/:slug(\\d{1,})/
مثلًا المسار blog/123/
وليس المسار blog/abc/
:
module.exports = {
async headers() {
return [
{
source: '/blog/:post(\\d{1,})',
headers: [
{
key: 'x-post',
value: ':post',
},
],
},
]
},
}
تُستخدم المحارف التالية (
و )
و {
و }
و :
و *
و +
و ?
في صياغة التعابير النمطية المستخدمة لمطابقة المسارات، فإن استُخدمت في الخاصية source
كقيم غير خاصة non-special لا بد من تجاوزها بإضافة الصيغة \\
قبلها:
module.exports = {
async headers() {
return [
{
// this will match `/english(default)/something` being requested
source: '/english\\(default\\)/:slug',
headers: [
{
key: 'x-header',
value: 'value',
},
],
},
]
},
}
مطابقة الترويسات وملفات تعريف الارتباط والاستعلام
بالإمكان مطابقة ترويسة عندما تُطابِق قيم ترويسة أو ملف تعريف الارتباط أو الاستعلام قيمة الحقل 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 headers() {
return [
// if the header `x-add-header` is present,
// the `x-another-header` header will be applied
{
source: '/:path*',
has: [
{
type: 'header',
key: 'x-add-header',
},
],
headers: [
{
key: 'x-another-header',
value: 'hello',
},
],
},
// if the source, query, and cookie are matched,
// the `x-authorized` header will be applied
{
source: '/specific/:path*',
has: [
{
type: 'query',
key: 'page',
// the page value will not be available in the
// header key/values since value is provided and
// doesn't use a named capture group e.g. (?<page>home)
value: 'home',
},
{
type: 'cookie',
key: 'authorized',
value: 'true',
},
],
headers: [
{
key: 'x-authorized',
value: ':authorized',
},
],
},
// if the header `x-authorized` is present and
// contains a matching value, the `x-another-header` will be applied
{
source: '/:path*',
has: [
{
type: 'header',
key: 'x-authorized',
value: '(?<authorized>yes|true)',
},
],
headers: [
{
key: 'x-another-header',
value: ':authorized',
},
],
},
// if the host is `example.com`,
// this header will be applied
{
source: '/:path*',
has: [
{
type: 'host',
value: 'example.com',
},
],
headers: [
{
key: 'x-another-header',
value: ':authorized',
},
],
},
]
},
}
الترويسات مع دعم للمسار الأساسي
عند استخدام basePath
مع إعادة الترويسات ستُضاف تلقائيًا البادئة إلى المعامل source
ما لم تضف الإعداد basePath: false
إلى دالة الترويسة:
module.exports = {
basePath: '/docs',
async headers() {
return [
{
source: '/with-basePath', // becomes /docs/with-basePath
headers: [
{
key: 'x-hello',
value: 'world',
},
],
},
{
source: '/without-basePath', // is not modified since basePath: false is set
headers: [
{
key: 'x-hello',
value: 'world',
},
],
basePath: false,
},
]
},
}
إعادة التوجيه مع دعم التوجه i18n
عند استخدام i18n
مع الرتويسات ستُضاف تلقائيًا البادئة المهيأة مسبقًا locales
إلى المعامل source
ما لم تضف الإعداد locale: false
إلى الترويسة. وفي حال استخدامه لا بد من إضافة تلك البادئة يدويًا حتى تُطابق بشكل صحيح:
module.exports = {
i18n: {
locales: ['en', 'fr', 'de'],
defaultLocale: 'en',
},
async headers() {
return [
{
source: '/with-locale', // automatically handles all locales
headers: [
{
key: 'x-hello',
value: 'world',
},
],
},
{
// does not handle locales automatically since locale: false is set
source: '/nl/with-locale-manual',
locale: false,
headers: [
{
key: 'x-hello',
value: 'world',
},
],
},
{
// this matches '/' since `en` is the defaultLocale
source: '/en',
locale: false,
headers: [
{
key: 'x-hello',
value: 'world',
},
],
},
{
// this gets converted to /(en|fr|de)/(.*) so will not match the top-level
// `/` or `/fr` routes like /:path* would
source: '/(.*)',
headers: [
{
key: 'x-hello',
value: 'world',
},
],
},
]
},
}
الترويسة Cache-Control
يمكن إعداد الترويسة Cache-Control
ضمن وجهات API الخاصة بتطبيقك عن طريق التابع res.setHeader
// pages/api/user.js
export default function handler(req, res) {
res.setHeader('Cache-Control', 's-maxage=86400')
res.status(200).json({ name: 'John Doe' })
}
لا يمكن إعداد ترويسات Cache-Control
ضمن الملف next.config.js
لأنها ستُلغى عند بناء نسخة الإنتاج، وذلك للتأكد من التخزين الفعّال لوجهات API والموجودات الساكنة.
إن أردت إعادة التحقق من الذاكرة المؤقتة لصفحة وُلِّدت بشكل ساكن، عليك ضبط الخاصية revalidate
ضمن الدالة getStaticProps
في الصفحة.
الامتدادات المخصصة للصفحات
وجهت هذه الامتدادات إلى وحدات برمجية مثل التي تضيف دعمًا للصفحات التي تحمل الامتداد . يمكن أن تهيئ الامتدادات التي يجري البحث عنها ضمن المجلد pages
عن تحليل الصفحة بفتح الملف next.config.js
وإضافة الإعداد pageExtensions
:
module.exports = {
pageExtensions: ['mdx', 'md', 'jsx', 'js', 'tsx', 'ts'],
}
ملاحظة: القيم الافتراضية للإعداد
pageExtensions
هي['tsx', 'ts', 'jsx', 'js']
.
ملاحظة: يؤثر تهيئة الإعداد
pageExtensions
على الملفاتdocument.js_
وapp.js_
وmiddleware.js
بالإضافة إلى الملفات ضمن/pages/api
. إذ يعني مثلًا الإعداد['page.tsx' و 'page.ts']:pageExtensions
، أن الملفاتdocument.tsx_
وapp.tsx_
وmiddleware.ts
وpages/users.tsx
وpages/api/users.ts
ينبغي أن تُعاد تسميتها إلىdocument.page.tsx_
وapp.page.tsx_
وmiddleware.page.ts
وpages/users.page.tsx
وpages/api/users.page.ts
.
تضمين الملفات من غير الصفحات في المجلد pages
لكي تشير إلى الموقع المشترك لملفات الاختبار و الملفات المولَّدة وغيرها من الملفات التي تستخدمها المكوّنات في المجلد pages
، بإمكانك وضع بادئة قبل الامتداد مثل page
. افتح الملف next.config.js
وأضف الإعداد pageExtensions
:
module.exports = {
pageExtensions: ['page.tsx', 'page.ts', 'page.jsx', 'page.js'],
}
أعد بعد ذلك تسمية صفحاتك لتضيف page.
إلى امتداداتها (غير اسم الملف MyPage.tsx
مثلًا إلى MyPage.page.tsx
)
ملاحظة: تأكد من إعادة تسمية الملفات
_document.js
و_app.js
وmiddleware.js
بالإضافة إلى الملفات الموجودة ضمنpages/api
.
ستفترض Next.js دون هذا الإعداد أن كل ملف tsx/ts/jsx/js ضمن المجلد pages
هو صفحة أو وجهة API، وقد تعرّض مسارات غير مقصودة لخطر هجمات رفض الخدمة denial of service أو أن ترمي خطأً كالتالي عند بناء تجميعة الإنتاج.
Build error occurred
Error: Build optimization failed: found pages without a React Component as default export in
pages/MyPage.generated
pages/MyPage.test
See https://nextjs.org/docs/messages/page-without-valid-component for more info.
شبكة إيصال المحتوى CDN مع الخيار assetPrefix
تنبيه: يهيئ النشر على تلقائيًا شبكة إيصال محتوى CDN لمشروع Next.js. لا حاجة لإعداد الخيار
assetPrefix
يدويًا.
ملاحظة: تدعم ابتداءً من النسخة +9.5 تخصيص مسار أساسي للتطبيق، وهذا يلائم استضافة تطبيقك ضمن مسار فرعي مثل
docs/
. لا حاجة في هذه الحالة إلى استخدامassetPrefix
.
لإعداد شبكة إيصال محتوى CDN يمكنك إعداد بادئة خاصة بالموجودات asset prefix وتهيئة أصل شبكة CDN لتحلل النطاق الذي يستضيف تطبيق Next.js.
افتح الملف next.config.js
وأضف الإعداد assetPrefix
:
const isProd = process.env.NODE_ENV === 'production'
module.exports = {
// استخدم شبكة إيصال المحتوى عند الإنتاج و الخادم المحلي عند التطوير
assetPrefix: isProd ? 'https://cdn.mydomain.com' : '',
}
تستخدم تلقائيًا بادئة الموجودات لملفات و التي تُحمَّل من المسار /_next/
(المجلد /next/static.
). سيصبح الطلب التالي:
/_next/static/chunks/4b9b41aaa062cbbfeff4add70f256968c51ece5d.4d708494b3aed70c04f0.js
لجزء من شيفرة JS مثلًا وفق الإعداد السابق كما يلي:
https://cdn.mydomain.com/_next/static/chunks/4b9b41aaa062cbbfeff4add70f256968c51ece5d.4d708494b3aed70c04f0.js
ستعتمد التهيئة الدقيقة لرفع ملفاتك إلى منظومة ايصال محتوى على المنظومة التي تختارها، وسيكون المجلد الوحيد الذي عليك استضافته على منظومة ايصال المحتوى هو محتوى المجلد .next/static/
الذي ينبغي رفعه ليكون على الشكل /next/static_
كما يشير طلب URL. لا ترفع بقية بقية ملفات المجلد /next.
فمن الخطأ عرض شيفرة الخادم وبقية الإعدادادت للعموم.
على الرغم أن الإعداد assetPrefix
يغطي الطلبات إلى /next/static_
لكنه لا يؤثر بالمسارات التالية:
- الملفات في المجلد ، فإن أردت تخديم هذه الموجودات عبر CDN، عليك تقديم البادئة بنفسك.
- طلبات
/next/data/_
إلى صفحاتgetServerSideProps
. إذ توجّه هذه الطلبات إلى النطاق الأساسي لكونها غير ساكنة.
- طلبات
/next/data/_
إلى صفحاتgetStaticProps
. إذ توجّه هذه الطلبات إلى النطاق الأساسي لدعم التوليد التدريجي الساكن حتى لو لم تكن تستخدمه (لأغراض الترابط).
الإعدادات المخصصة لمحزّم Webpack
ملاحظة: لم تُغطى التغييرات في إعداد webpack بواسطة مخططات الإصدار SemVer لهذا تابع على مسؤوليتك الشخصية.
قبل أن تتابع بإضافة إعدادات webpack مخصصة إلى تطبيقك تأكد أولًا من عدم دعم Next.js للحالة التي تواجهك:
تتاح بعض الميزات التي تُطلب بكثرة على شكل إضافات:
لكي توسّع طريقة استخدامنا لمحزّم webpack
، بإمكانك تعريف دالة توسّع إعداداته ضمن الملف next.config.js
كالتالي:
module.exports = {
webpack: (
config,
{ buildId, dev, isServer, defaultLoaders, nextRuntime, webpack }
) => {
// Important: return the modified config
return config
},
}
تُنفَّذ الدالة
webpack
مرتين، الأولى للخادم والثانية للعميل. يتيح لك ذلك التمييز بين إعدادات الخادم و العميل من خلال الخاصيةisServer
إن الوسيط الثاني للدالة webpack
هو كائن له الخاصيات التالية:
buildId
: من النوعstring
، معرّف البناء، ويُستخدم كمعرّف فريد بين نسخ البناء.dev
: قيمة منطقية تشير إلى أن التصريف سيجري في بيئة التطوير أم لا.isServer
: قيمة منطقيةtrue
إن كان التصريف من جانب الخادم وfalse
من جانب العميل.nextRuntime
: من أحد النوعينString | undefined
، وتحدد منصة تنفيذ الشيفرة عندما يجري التصريف من جانب الخادم وتاخذ إحدى القيمتين"edge"
أو"nodejs"
بينما تأخذ القيمةundefined
عند التصريف من جانب العميل.defaultLoaders
: من النوعobject
، وتشير إلى المحمّلات الافتراضية في تستخدم داخليًا في Next.js:babel
: من النوعobject
وتحمّل تهيئةbabel-loader
إليك مثال عن استخدام defaultLoaders.babel
:
//babel-loader مثال عن إضافة محمّل يعتمد على
// @next/mdx المصدر مأخوذ من الإضافة
// https://github.com/vercel/next.js/tree/canary/packages/next-mdx
module.exports = {
webpack: (config, options) => {
config.module.rules.push({
test: /\.mdx/,
use: [
options.defaultLoaders.babel,
{
loader: '@mdx-js/loader',
options: pluginOptions.options,
},
],
})
return config
},
}
الخاصية nextRuntime
تأخذ isServer
القيمة true
عندما تكون قيمة nextRuntime
هي "edge"
أو "nodejs"
. وتستخدم الحالة التي تكون فيها قيمة nextRuntime
هي edge
حاليًا مع الأداة الوسطية ومكوّنات الخادم في منصة التشغيل الحدودية فقط.
المصادر
- الصفحات Next.config.js من توثيق Next.js الرسمي.