كيفية إنشاء تصدير ثابت لتطبيق Next.js الخاص بك

يتيح Next.js البدء كموقع ثابت أو تطبيق صفحة واحدة (SPA)، ثم الترقية لاحقًا بشكل اختياري لاستخدام الميزات التي تتطلب خادمًا.

عند تشغيل next build، يقوم Next.js بإنشاء ملف HTML لكل مسار. من خلال تقسيم تطبيق SPA صارم إلى ملفات HTML فردية، يمكن لـ Next.js تجنب تحميل أكواد JavaScript غير الضرورية على جانب العميل، مما يقلل حجم الحزمة ويُمكن من تحميل الصفحات بشكل أسرع.

بما أن Next.js يدعم هذا التصدير الثابت، فيمكن نشره واستضافته على أي خادم ويب يمكنه تقديم أصول ثابتة من نوع HTML/CSS/JS.

التكوين

لتمكين التصدير الثابت، قم بتغيير وضع الإخراج داخل next.config.js:

next.config.js
/**
 * @type {import('next').NextConfig}
 */
const nextConfig = {
  output: 'export',

  // اختياري: تغيير الروابط من `/me` إلى `/me/` وإنشاء `/me.html` كـ `/me/index.html`
  // trailingSlash: true,

  // اختياري: منع التحويل التلقائي من `/me` إلى `/me/`، والحفاظ على `href` كما هو
  // skipTrailingSlashRedirect: true,

  // اختياري: تغيير دليل الإخراج من `out` إلى `dist`
  // distDir: 'dist',
}

module.exports = nextConfig

بعد تشغيل next build، سيقوم Next.js بإنشاء مجلد out يحتوي على أصول HTML/CSS/JS لتطبيقك.

الميزات المدعومة

تم تصميم نواة Next.js لدعم التصدير الثابت.

مكونات الخادم

عند تشغيل next build لإنشاء تصدير ثابت، ستتم معالجة مكونات الخادم المستخدمة داخل دليل app أثناء عملية البناء، بشكل مشابه لإنشاء المواقع الثابتة التقليدية.

سيتم تحويل المكون الناتج إلى HTML ثابت لتحميل الصفحة الأولية وحمولة ثابتة للتنقل بين المسارات على جانب العميل. لا توجد حاجة لتغيير مكونات الخادم عند استخدام التصدير الثابت، إلا إذا كانت تستخدم وظائف الخادم الديناميكية.

export default async function Page() {
  // سيتم تنفيذ عملية الجلب هذه على الخادم أثناء `next build`
  const res = await fetch('https://api.example.com/...')
  const data = await res.json()

  return <main>...</main>
}

مكونات العميل

إذا كنت تريد تنفيذ جلب البيانات على جانب العميل، يمكنك استخدام مكون عميل مع SWR لتخزين الطلبات مؤقتًا.

'use client'

import useSWR from 'swr'

const fetcher = (url: string) => fetch(url).then((r) => r.json())

export default function Page() {
  const { data, error } = useSWR(
    `https://jsonplaceholder.typicode.com/posts/1`,
    fetcher
  )
  if (error) return 'فشل التحميل'
  if (!data) return 'جاري التحميل...'

  return data.title
}
'use client'

import useSWR from 'swr'

const fetcher = (url) => fetch(url).then((r) => r.json())

export default function Page() {
  const { data, error } = useSWR(
    `https://jsonplaceholder.typicode.com/posts/1`,
    fetcher
  )
  if (error) return 'فشل التحميل'
  if (!data) return 'جاري التحميل...'

  return data.title
}

نظرًا لأن انتقالات المسار تتم على جانب العميل، فإن هذا يعمل مثل تطبيق SPA تقليدي. على سبيل المثال، مسار الفهرس التالي يسمح لك بالتنقل بين منشورات مختلفة على جانب العميل:

import Link from 'next/link'

export default function Page() {
  return (
    <>
      <h1>صفحة الفهرس</h1>
      <hr />
      <ul>
        <li>
          <Link href="/post/1">المنشور 1</Link>
        </li>
        <li>
          <Link href="/post/2">المنشور 2</Link>
        </li>
      </ul>
    </>
  )
}
import Link from 'next/link'

export default function Page() {
  return (
    <>
      <h1>صفحة الفهرس</h1>
      <p>
        <Link href="/other">صفحة أخرى</Link>
      </p>
    </>
  )
}

تحسين الصور

يمكن استخدام تحسين الصور عبر next/image مع التصدير الثابت عن طريق تحديد محمل صور مخصص في next.config.js. على سبيل المثال، يمكنك تحسين الصور باستخدام خدمة مثل Cloudinary:

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export',
  images: {
    loader: 'custom',
    loaderFile: './my-loader.ts',
  },
}

module.exports = nextConfig

سيحدد هذا المحمل المخصص كيفية جلب الصور من مصدر بعيد. على سبيل المثال، المحمل التالي سيقوم ببناء URL لـ Cloudinary:

export default function cloudinaryLoader({
  src,
  width,
  quality,
}: {
  src: string
  width: number
  quality?: number
}) {
  const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality || 'auto'}`]
  return `https://res.cloudinary.com/demo/image/upload/${params.join(
    ','
  )}${src}`
}
export default function cloudinaryLoader({ src, width, quality }) {
  const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality || 'auto'}`]
  return `https://res.cloudinary.com/demo/image/upload/${params.join(
    ','
  )}${src}`
}

يمكنك بعد ذلك استخدام next/image في تطبيقك، مع تحديد مسارات نسبية للصورة في Cloudinary:

import Image from 'next/image'

export default function Page() {
  return <Image alt="سلاحف" src="/turtles.jpg" width={300} height={300} />
}
import Image from 'next/image'

export default function Page() {
  return <Image alt="سلاحف" src="/turtles.jpg" width={300} height={300} />
}

معالجات المسار

سيقوم معالجات المسار (Route Handlers) بعرض استجابة ثابتة عند تشغيل next build. فقط فعل HTTP GET مدعوم. يمكن استخدام هذا لإنشاء ملفات ثابتة من نوع HTML أو JSON أو TXT أو غيرها من البيانات المخزنة مؤقتًا أو غير المخزنة. على سبيل المثال:

export async function GET() {
  return Response.json({ name: 'لي' })
}
export async function GET() {
  return Response.json({ name: 'لي' })
}

سيقوم الملف أعلاه app/data.json/route.ts بإنشاء ملف ثابت أثناء next build، منتجًا data.json يحتوي على { name: 'لي' }.

إذا كنت بحاجة إلى قراءة قيم ديناميكية من الطلب الوارد، فلا يمكنك استخدام التصدير الثابت.

واجهات برمجة تطبيقات المتصفح

يتم تحويل مكونات العميل إلى HTML مسبقًا أثناء next build. نظرًا لأن واجهات برمجة تطبيقات الويب مثل window و localStorage و navigator غير متوفرة على الخادم، فأنت بحاجة إلى الوصول إلى هذه الواجهات بأمان فقط عند التشغيل في المتصفح. على سبيل المثال:

'use client';

import { useEffect } from 'react';

export default function ClientComponent() {
  useEffect(() => {
    // لديك الآن حق الوصول إلى `window`
    console.log(window.innerHeight);
  }, [])

  return ...;
}

الميزات غير المدعومة

الميزات التي تتطلب خادم Node.js، أو المنطق الديناميكي الذي لا يمكن حسابه أثناء عملية البناء، غير مدعومة:

محاولة استخدام أي من هذه الميزات مع next dev سيؤدي إلى خطأ، مشابه لتعيين الخيار dynamic إلى error في تخطيط الجذر.

export const dynamic = 'error'

النشر

مع التصدير الثابت، يمكن نشر Next.js واستضافته على أي خادم ويب يمكنه تقديم أصول ثابتة من نوع HTML/CSS/JS.

عند تشغيل next build، يقوم Next.js بإنشاء التصدير الثابت في مجلد out. على سبيل المثال، لنفترض أن لديك المسارات التالية:

  • /
  • /blog/[id]

بعد تشغيل next build، سيقوم Next.js بإنشاء الملفات التالية:

  • /out/index.html
  • /out/404.html
  • /out/blog/post-1.html
  • /out/blog/post-2.html

إذا كنت تستخدم مضيفًا ثابتًا مثل Nginx، يمكنك تكوين إعادة كتابة من الطلبات الواردة إلى الملفات الصحيحة:

nginx.conf
server {
  listen 80;
  server_name acme.com;

  root /var/www/out;

  location / {
      try_files $uri $uri.html $uri/ =404;
  }

  # هذا ضروري عندما `trailingSlash: false`.
  # يمكنك حذف هذا عندما `trailingSlash: true`.
  location /blog/ {
      rewrite ^/blog/(.*)$ /blog/$1.html break;
  }

  error_page 404 /404.html;
  location = /404.html {
      internal;
  }
}

سجل الإصدارات

الإصدارالتغييرات
v14.0.0تمت إزالة next export لصالح "output": "export"
v13.4.0أضاف موجه التطبيق (المستقر) دعمًا محسنًا للتصدير الثابت، بما في ذلك استخدام مكونات خادم React ومعالجات المسار.
v13.3.0تم إهمال next export واستبداله بـ "output": "export"