Next.js: التصيير المسبق الجزئي (PPR)

آخر تحديث: 2026-08-26

PPR هو أكثر أوضاع التصيير ثورية في Next.js 16 — يتيح للصفحة الواحدة الجمع بين استجابة الهيكل الثابت فائقة السرعة والمحتوى الفوري في الأقسام الديناميكية.

1. ما ستتعلمه



2. قصة حقيقية لمهندسة معمارية

(1) نقطة الألم: لوحة التحكم إما ثابتة بالكامل (بيانات قديمة) أو ديناميكية بالكامل (بطيئة)

ديانا هي مهندسة معمارية في فريق TaskFlow. تواجه لوحة تحكم SaaS الخاصة بالشركة معضلة:

ما تريده هو أن يكون شريط التنقل/الشريط الجانبي/التخطيط ثابتًا (يتم توليده وقت البناء وتخزينه مؤقتًا عبر CDN)، وأن تكون بيانات المستخدم/الإشعارات ديناميكية (يتم جلبها في الوقت الفعلي). لكن SSG أو SSR التقليديين يسمحان بواحد فقط منهما — لا يمكن للصفحة الواحدة أن يكون لها سوى وضع تصيير واحد.

(2) حل مشكلة PPR

استخدم PPR لتقسيم لوحة التحكم إلى هيكل ثابت (التخطيط + التنقل) ومنطقة ديناميكية (حدود Suspense).

TSX
// app/dashboard/page.tsx
import { Suspense } from 'react'
import { NavBar } from '@/components/NavBar'
import { UserGreeting } from '@/components/UserGreeting'
import { NotificationList } from '@/components/NotificationList'
import { Skeleton } from '@/components/Skeleton'

export default function DashboardPage() {
  return (
    <div>
      <NavBar />                           {/* الهيكل الثابت: يتم التوليد أثناء البناء */}
      <Suspense fallback={<Skeleton />}>  {/* الحد الديناميكي */}
        <UserGreeting />
      </Suspense>
      <Suspense fallback={<Skeleton />}>
        <NotificationList />
      </Suspense>
    </div>
  )
}

(3) النتائج

البُعد SSR النقي SSG النقي PPR
زمن أول بايت (TTFB) 4 ثوانٍ 50 مللي ثانية 50 مللي ثانية
حداثة البيانات ✅ الأحدث ❌ لقطة وقت البناء ✅ فوري للمناطق الديناميكية
وحدة المعالجة المركزية للخادم 85% < 5% < 15%
التخزين المؤقت CDN ❌ غير مدعوم ✅ الصفحة كاملة ✅ الهيكل الثابت
تعقيد التنفيذ منخفض منخفض منخفض (فقط أضف Suspense)


3. مفاهيم PPR وتكوينه

المفهوم الأساسي لـ PPR (التصيير المسبق الجزئي) هو أن الصفحة يمكن أن تحتوي على هيكل ثابت تم تصييره مسبقًا ومناطق ديناميكية متدفقة. يتم توليد الأجزاء الثابتة أثناء وقت البناء، بينما يتم تصيير الأجزاء الديناميكية حسب الطلب.

(1) الهيكل الثابت + الحد الديناميكي

100%
graph TB
    subgraph "صفحة PPR"
        A[الهيكل الثابت<br/>التخطيط + التنقل]
        B[حد Suspense 1<br/>معلومات المستخدم - ديناميكي]
        C[حد Suspense 2<br/>قائمة الإشعارات - ديناميكي]
        D[حد Suspense 3<br/>الرسوم البيانية الفورية - ديناميكي]
    end

    A --> B
    A --> C
    A --> D

    style A fill:#d4edda
    style B fill:#cce5ff
    style C fill:#cce5ff
    style D fill:#cce5ff
نوع المكون توقيت التصيير استراتيجية التخزين المؤقت المكونات النموذجية
الهيكل الثابت أثناء البناء تخزين CDN المؤقت التخطيط، شريط التنقل، التذييل، الشريط الجانبي، الشعار
الحد الديناميكي عند الطلب غير مخزن مؤقتًا (أو تخزين قصير المدى) تحية المستخدم، قائمة الإشعارات، الرسوم البيانية الفورية، البحث

(2) تفعيل PPR

PPR معطل افتراضيًا في Next.js 16 ويجب تفعيله في next.config.ts:

TS
// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  experimental: {
    ppr: true  // تفعيل PPR
  }
}

export default nextConfig
BASH
# تشغيل خادم التطوير بعد التثبيت
npm run dev
⚠️ ملاحظة: يتطلب PPR إصدار Next.js 16.2 أو أحدث. بمجرد التفعيل، ستستفيد جميع الصفحات التي لا تحتوي على dynamic = 'force-dynamic' أو cache: 'no-store' تلقائيًا من تحسين PPR افتراضيًا. سترى السجل التالي أثناء البناء: ✓ PPR enabled for /dashboard

▶ مثال: مقارنة PPR مع عدم تفعيله (المستوى: ⭐)

الناتج:

TEXT 📖 للعرض فقط
يعرض مكون React واجهة المستخدم الموصوفة في المتصفح.
TSX
// app/ppr-compare/page.tsx — بعد تفعيل PPR يسري تلقائيًا
import { Suspense } from 'react'

// قسم الهيكل الثابت (يتم التوليد أثناء البناء)
export default function PprComparePage() {
  return (
    <div>
      <header style={{ background: '#f0f0f0', padding: 16 }}>
        <h1>PPR Demo</h1>
        <nav><a href="/">الرئيسية</a> | <a href="/about">حول</a></nav>
      </header>

      {/* الحد الديناميكي: يتم الاسترجاع من جديد مع كل طلب */}
      <Suspense fallback={<div style={{ padding: 16 }}>جارٍ تحميل المستخدم...</div>}>
        <RealtimeUser />
      </Suspense>

      {/* الحد الثابت: يتم التصيير أثناء البناء */}
      <footer style={{ borderTop: '1px solid #ddd', padding: 16 }}>
        <p>تم البناء في: {new Date().toISOString()}</p>
      </footer>
    </div>
  )
}

async function RealtimeUser() {
  const user = await fetch('https://api.example.com/me', { cache: 'no-store' })
    .then(r => r.json())
  return <div style={{ padding: 16 }}>مرحبًا، {user.name}</div>
}

الناتج:

TEXT 📖 للعرض فقط
يعرض: لوحة تحكم بهيكل جانبي ثابت + ملف مستخدم ديناميكي وإحصائيات مشروع داخل حدود Suspense.


4. Suspense: الحدود كحدود ديناميكية

المبدأ الأساسي لـ PPR: محتوى كل غلاف <Suspense> هو حد يتم تصييره ديناميكيًا. الأقسام غير المغلفة بـ Suspense هي هياكل ثابتة يتم تصييرها مسبقًا أثناء وقت البناء.

(1) قواعد الحدود

100%
graph LR
    A[مكونات الصفحة] --> B[محتوى ثابت<br/>بدون Suspense]
    A --> C[حد Suspense]
    C --> D[مكونات فرعية ديناميكية<br/>تصيير جديد مع كل طلب]
    A --> E[حد Suspense آخر]
    E --> F[مناطق ديناميكية منفصلة]
حالة التغليف سلوك PPR مثال
✅ مغلف بـ <Suspense> ديناميكي — يتم التصيير عند الطلب، محتوى فوري معلومات المستخدم، بيانات المخزون
❌ بدون <Suspense> ثابت — يتم التصيير أثناء البناء، مخزن مؤقتًا في CDN شريط التنقل، التذييل، الشعار

(2) تجنب Suspense غير الضروري

إذا كان المكون لا يحتاج إلى بيانات فورية، فلا تغلفه بـ Suspense — بهذه الطريقة، سيصبح جزءًا من الهيكل الثابت.

TSX
// app/dashboard/page.tsx
export default function DashboardPage() {
  return (
    <div>
      {/* ✅ ثابت: الشريط الجانبي موجود دائمًا، لا يتطلب تحديثات فورية */}
      <Sidebar />

      {/* ✅ ديناميكي: يحتاج إلى استرجاع فوري */}
      <Suspense fallback={<LoadingSpinner />}>
        <RealtimeData />
      </Suspense>

      {/* ❌ Suspense غير ضروري: هذا المكون لا يحتوي على بيانات ديناميكية. */}
      <Suspense fallback={<LoadingSpinner />}>
        <StaticAboutSection />  {/* لا يحتاج إلى تغليف */}
      </Suspense>
    </div>
  )
}

▶ مثال: تسلسل التحميل لحدود Suspense المتعددة (المستوى: ⭐⭐)

الناتج:

TEXT 📖 للعرض فقط
يعرض هيكلًا ثابتًا فورًا، مع تحميل المحتوى الديناميكي داخل حدود Suspense.
الاحتياطي: }>
النص المرئي: }> | }>
TSX
// app/ppr-timing/page.tsx
import { Suspense } from 'react'

export default function PprTimingPage() {
  return (
    <div>
      <h1>PPR Timing Demo</h1>
      {/* الهيكل الثابت: يظهر فورًا */}
      <p>يظهر هذا فورًا (هيكل ثابت)</p>

      {/* الحد الديناميكي 1: يظهر بعد ثانيتين */}
      <Suspense fallback={<div>⏳ جارٍ تحميل القسم 1...</div>}>
        <DelayedSection label="القسم 1" delay={2000} />
      </Suspense>

      {/* الحد الديناميكي 2: يظهر بعد 4 ثوانٍ، مستقل عن الحد 1 */}
      <Suspense fallback={<div>⏳ جارٍ تحميل القسم 2...</div>}>
        <DelayedSection label="القسم 2" delay={4000} />
      </Suspense>
    </div>
  )
}

async function DelayedSection({ label, delay }: { label: string; delay: number }) {
  await new Promise(resolve => setTimeout(resolve, delay))
  return <div>✅ {label} تم التحميل بعد {delay} مللي ثانية</div>
}

الناتج:

TEXT 📖 للعرض فقط
يعرض هيكلًا ثابتًا فورًا، مع تحميل المحتوى الديناميكي داخل حدود Suspense.
الاحتياطي: ⏳ جارٍ تحميل القسم 1...
النص المرئي: PPR Timing Demo | يظهر هذا فورًا (هيكل ثابت) | ⏳ جارٍ تحميل القسم 1... | }>


5. مقارنة أداء PPR و ISR و SSR

لكل من أوضاع التصيير الثلاثة حالات استخدام خاصة به، ويملأ PPR الفجوة لسيناريوهات "ثابت جزئيًا + ديناميكي جزئيًا".

البُعد SSR ISR PPR
توقيت التصيير مع كل طلب البناء + حسب الطلب في الخلفية البناء (الهيكل الثابت) + الطلب (المحتوى الديناميكي)
استراتيجية التخزين المؤقت لا يتم التخزين المؤقت عبر CDN تخزين كامل للصفحة عبر CDN تخزين الهيكل الثابت عبر CDN + المناطق الديناميكية غير مخزنة
الفورية ✅ الأحدث ⚠️ أقصى تأخير = revalidate ✅ فوري للمناطق الديناميكية
حمل الخادم مرتفع منخفض منخفض (يصيّر الأجزاء الديناميكية فقط)
حالات الاستخدام صفحات التخصيص والمصادقة مواقع المحتوى والمدونات لوحات التحكم والصفحات الهجينة
زمن العرض الأول بطيء (انتظار التصيير من جانب الخادم) سريع (مخزن مؤقتًا) سريع (هيكل ثابت، فوري)

(1) توصيات الاختيار

100%
graph TB
    A[هذه الصفحة تتطلب؟] --> B{بيانات فورية؟}
    B -->|الصفحة بأكملها تُحدث فوريًا| C[SSR]
    B -->|فوري في بعض المناطق| D[PPR]
    B -->|لا توجد بيانات فورية| E{تكرار التحديث؟}
    E -->|متكرر| F[ISR]
    E -->|شبه ثابت| G[SSG]

    style D fill:#d4edda

▶ مثال: تصميم مكونات لوحة تحكم PPR (المستوى: ⭐⭐⭐)

الناتج:

TEXT 📖 للعرض فقط
مخطط استراتيجية التصيير: هيكل ثابت + حدود Suspense ديناميكية.
TSX
// app/dashboard-ppr/page.tsx — تصميم PPR عملي
import { Suspense } from 'react'

// ======== مجموعة الهيكل الثابت ========
function DashboardHeader() {
  return (
    <header style={{ background: '#1a1a2e', color: 'white', padding: '16px 24px' }}>
      <h1 style={{ margin: 0 }}>TaskFlow Dashboard</h1>
    </header>
  )
}

function Sidebar() {
  return (
    <nav style={{ width: 240, background: '#f5f5f5', padding: 16, minHeight: 'calc(100vh - 64px)' }}>
      <ul style={{ listStyle: 'none', padding: 0 }}>
        <li><a href="/">🏠 الرئيسية</a></li>
        <li><a href="/projects">📁 المشاريع</a></li>
        <li><a href="/tasks">✅ المهام</a></li>
        <li><a href="/analytics">📊 التحليلات</a></li>
        <li><a href="/settings">⚙️ الإعدادات</a></li>
      </ul>
    </nav>
  )
}

// ======== مكونات الحد الديناميكي ========
async function UserProfile() {
  const user = await fetch('https://api.example.com/me', { cache: 'no-store' }).then(r => r.json())
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
      <img src={user.avatar} alt="" style={{ borderRadius: '50%', width: 40, height: 40 }} />
      <div>
        <strong>{user.name}</strong>
        <p style={{ margin: 0, fontSize: 12, color: '#666' }}>{user.role}</p>
      </div>
    </div>
  )
}

async function ProjectStats() {
  const stats = await fetch('https://api.example.com/stats', { next: { revalidate: 60 } }).then(r => r.json())
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 16 }}>
      <StatCard label="المشاريع النشطة" value={stats.activeProjects} color="#4caf50" />
      <StatCard label="المهام المعلقة" value={stats.pendingTasks} color="#ff9800" />
      <StatCard label="المكتملة" value={stats.completed} color="#2196f3" />
    </div>
  )
}

function StatCard({ label, value, color }: { label: string; value: number; color: string }) {
  return (
    <div style={{ border: `1px solid ${color}`, borderRadius: 8, padding: 16, textAlign: 'center' }}>
      <p style={{ fontSize: 28, fontWeight: 'bold', color, margin: 0 }}>{value}</p>
      <p style={{ margin: 0, color: '#666', fontSize: 14 }}>{label}</p>
    </div>
  )
}

export default function DashboardPprPage() {
  return (
    <div style={{ display: 'flex' }}>
      <Sidebar />
      <main style={{ flex: 1, padding: 24 }}>
        <DashboardHeader />
        <Suspense fallback={<div>جارٍ تحميل الملف الشخصي...</div>}>
          <UserProfile />
        </Suspense>
        <div style={{ height: 24 }} />
        <Suspense fallback={<div>جارٍ تحميل الإحصائيات...</div>}>
          <ProjectStats />
        </Suspense>
      </main>
    </div>
  )
}

الناتج:

TEXT 📖 للعرض فقط
يجلب البيانات من جانب الخادم ويعرض النتيجة في الصفحة.
المحتوى المرئي: TaskFlow Dashboard


6. مثال كامل: لوحة تحكم التجارة الإلكترونية باستخدام PPR

TSX
// app/ppr-ecommerce/page.tsx — لوحة تحكم التجارة الإلكترونية PPR
import { Suspense } from 'react'
import { cookies } from 'next/headers'

// ======== الهيكل الثابت ========
function StoreHeader() {
  return (
    <header style={{ background: '#2c3e50', color: 'white', padding: '12px 24px', display: 'flex', justifyContent: 'space-between' }}>
      <strong>Store Dashboard</strong>
      <span>تم البناء: {new Date().toISOString().split('T')[0]}</span>
    </header>
  )
}

function Navigation() {
  return (
    <nav style={{ background: '#34495e', padding: '8px 24px', display: 'flex', gap: 24, color: 'white' }}>
      <a href="/ppr-ecommerce" style={{ color: 'white' }}>نظرة عامة</a>
      <a href="/ppr-ecommerce/orders" style={{ color: 'white' }}>الطلبات</a>
      <a href="/ppr-ecommerce/products" style={{ color: 'white' }}>المنتجات</a>
    </nav>
  )
}

// ======== المكونات الديناميكية ========
async function LiveOrderFeed() {
  const orders = await fetch('https://api.example.com/orders/recent', {
    cache: 'no-store'
  }).then(r => r.json())

  return (
    <div style={{ border: '1px solid #ddd', borderRadius: 8, padding: 16 }}>
      <h2>الطلبات المباشرة ({orders.length})</h2>
      <table style={{ width: '100%', borderCollapse: 'collapse' }}>
        <thead><tr><th>الطلب</th><th>العميل</th><th>الحالة</th><th>الإجمالي</th></tr></thead>
        <tbody>
          {orders.map((o: any) => (
            <tr key={o.id}>
              <td>#{o.id}</td>
              <td>{o.customer}</td>
              <td><StatusBadge status={o.status} /></td>
              <td>${o.total}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  )
}

function StatusBadge({ status }: { status: string }) {
  const colors: Record<string, string> = {
    pending: '#ff9800', shipped: '#2196f3', delivered: '#4caf50', cancelled: '#f44336'
  }
  return <span style={{ background: colors[status] ?? '#ccc', color: 'white', padding: '2px 8px', borderRadius: 12, fontSize: 12 }}>{status}</span>
}

async function RevenueWidget() {
  const revenue = await fetch('https://api.example.com/revenue/today', {
    next: { revalidate: 300 }
  }).then(r => r.json())

  return (
    <div style={{ background: 'linear-gradient(135deg, #667eea, #764ba2)', color: 'white', borderRadius: 8, padding: 24 }}>
      <h2 style={{ margin: 0, fontSize: 14, opacity: 0.8 }}>إيرادات اليوم</h2>
      <p style={{ fontSize: 36, fontWeight: 'bold', margin: '8px 0' }}>${revenue.total}</p>
      <p style={{ margin: 0, fontSize: 12, opacity: 0.8 }}>↑ {revenue.growth}% مقارنة بالأمس</p>
    </div>
  )
}

async function TopProducts() {
  const products = await fetch('https://api.example.com/products/top', {
    next: { tags: ['top-products'] }
  }).then(r => r.json())

  return (
    <div style={{ border: '1px solid #ddd', borderRadius: 8, padding: 16 }}>
      <h2>أفضل المنتجات</h2>
      <ol>{products.slice(0, 5).map((p: any) => (
        <li key={p.id}>{p.name} — {p.sold} مباع</li>
      ))}</ol>
    </div>
  )
}

// ======== الصفحة (PPR مختلط) ========
export default function PprEcommercePage() {
  return (
    <div>
      <StoreHeader />
      <Navigation />
      <main style={{ padding: 24, display: 'grid', gap: 24, gridTemplateColumns: '2fr 1fr' }}>
        <div>
          <Suspense fallback={<div>جارٍ تحميل الطلبات...</div>}>
            <LiveOrderFeed />
          </Suspense>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
          <Suspense fallback={<div>جارٍ تحميل الإيرادات...</div>}>
            <RevenueWidget />
          </Suspense>
          <Suspense fallback={<div>جارٍ تحميل المنتجات...</div>}>
            <TopProducts />
          </Suspense>
        </div>
      </main>
    </div>
  )
}

▶ مثال: التحقق من مخرجات البناء (المستوى: ⭐)

BASH
# عرض مخرجات الهيكل الثابت بعد البناء
npm run build

# في .next/server/app/ppr-ecommerce، انتقل للأسفل للعرض
ls .next/server/app/ppr-ecommerce/

الناتج:

TEXT 📖 للعرض فقط
page.html          ← HTML الهيكل الثابت (شريط التنقل، التخطيط)
page.rsc           ← حمولة RSC (القسم الثابت)
page_stream.html   ← نقطة الحقن الجزئي المتدفق

▶ مثال: التحقق من الهيكل الثابت PPR + البيانات الديناميكية (المستوى: ⭐⭐)

الناتج:

TEXT 📖 للعرض فقط
يتم تصيير الصفحة كما هو موصوف أعلاه، مع تحديث واجهة المستخدم بناءً على السلوك الموصوف.
TSX
// app/ppr-verify/page.tsx
import { Suspense } from 'react'

function StaticHeader() {
  return (
    <header style={{ borderBottom: '2px solid #333', padding: 16, marginBottom: 16 }}>
      <h1>PPR Verification Page</h1>
      <p>الطابع الزمني للبناء: {new Date().toISOString()}</p>
      <nav><a href="/">الرئيسية</a> | <a href="/ppr-verify">تحديث</a></nav>
    </header>
  )
}

async function DynamicContent() {
  const res = await fetch('http://worldtimeapi.org/api/timezone/Etc/UTC', {
    cache: 'no-store'
  }).then(r => r.json())

  return (
    <div style={{ background: '#e3f2fd', padding: 16, borderRadius: 8 }}>
      <h2>وقت الخادم المباشر</h2>
      <p style={{ fontSize: 24 }}>{res.datetime}</p>
    </div>
  )
}

export default function PprVerifyPage() {
  return (
    <div>
      <StaticHeader />
      <Suspense fallback={<div style={{ padding: 16 }}>⏳ جارٍ تحميل الوقت المباشر...</div>}>
        <DynamicContent />
      </Suspense>
      <footer style={{ marginTop: 32, color: '#666' }}>
        <p>الرأس هو هيكل ثابت (مخزن مؤقتًا). المحتوى الديناميكي يتحدث مع كل طلب.</p>
      </footer>
    </div>
  )
}

الناتج:

TEXT 📖 للعرض فقط
يجلب البيانات من جانب الخادم ويعرض النتيجة في الصفحة.
المحتوى المرئي: PPR Verification Page

❓ أسئلة شائعة

س ما الفرق بين PPR و Streaming SSR؟
ج في Streaming SSR، يحدث التصيير فقط عند تقديم طلب، وكل حد Suspense هو "ديناميكي". أما الهيكل الثابت في PPR فيتم توليده أثناء وقت البناء — يرى المستخدمون HTML المخزن مؤقتًا على CDN، وليس تصييرًا فوريًا من جانب الخادم. PPR = هيكل ثابت + مناطق ديناميكية متدفقة.
س هل ستتحول جميع الصفحات إلى PPR بعد تفعيله؟
ج نعم، افتراضيًا، جميع المسارات التي لا تحتوي على dynamic = 'force-dynamic' ستستخدم PPR. ومع ذلك، فقط الصفحات التي تحتوي على حدود <Suspense> سيكون لها تأثير "الهيكل الثابت + المنطقة الديناميكية". الصفحات بدون Suspense ستظل ثابتة تمامًا (سلوك SSG).
س هل يمكن تداخل المناطق الديناميكية في PPR؟
ج نعم. يمكن تداخل حدود Suspense — يعتمد Suspense الداخلي على الخارجي. ومع ذلك، يُنصح بالحفاظ على هيكل مسطح (2-5 حدود Suspense لكل صفحة)، حيث يمكن أن يؤدي التداخل المفرط إلى ترتيب تحميل معقد.
س كيف يتم تحديث البيانات في الهيكل الثابت لـ PPR؟
ج يتم قفل البيانات في الهيكل الثابت عند وقت البناء. إذا قمت بعرض اسم مستخدم في التخطيط، فهو لقطة من وقت البناء. الحل: ضع البيانات الخاصة بالمستخدم داخل حد Suspense ديناميكي، واحتفظ بالهيكل الثابت للمحتوى الذي لا يتغير أبدًا (مثل الشعارات وروابط التنقل وهيكل التخطيط).
س هل يمكن أن يتعايش PPR مع أوضاع التصيير الأخرى؟
ج نعم. داخل نفس التطبيق، يمكن أن يكون لديك صفحات PPR (لوحة التحكم)، وصفحات SSG (التسويق)، وصفحات ISR (المدونة)، وصفحات SSR (الإدارة). يتم تكوين أوضاع التصيير على مستوى المسار — كل مقطع مسار يتم تكوينه بشكل مستقل.
س كيف يتصرف PPR في وضع التطوير؟
ج لا يزال PPR يعمل في وضع التطوير، ولكن نظرًا لأن كل طلب يُعاد بناؤه من الصفر، فإن فوائد الهيكل الثابت (التخزين المؤقت عبر CDN) لا تكون ملحوظة محليًا. لن تختبر حقًا تعزيز الأداء من PPR إلا بعد بناء الإنتاج. يمكنك تشغيل npm run build && npm run start لاختبار وضع الإنتاج.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (⭐): قم بتفعيل PPR في next.config.ts، وأنشئ app/ppr-basic/page.tsx يحتوي على قسم <header> ثابت وقسم <Suspense> ديناميكي (استدعاء API لاسترجاع الوقت الحالي)، وتحقق من أن مخرجات البناء تتضمن HTML ثابت.

  2. تمرين متقدم (⭐⭐): أنشئ app/ppr-dashboard/page.tsx يحتوي على 3 حدود Suspense على الأقل (معلومات المستخدم، قائمة الإشعارات، إحصائيات فورية)، لكل منها fallback خاص به. تأكد من أن شريط التنقل والشريط الجانبي هما هياكل ثابتة. بعد البناء، تحقق من أن page.html يتضمن شريط التنقل لكنه لا يحتوي على أي محتوى ديناميكي.

  3. تحدي (⭐⭐⭐): قم ببناء لوحة تحكم متعددة الصفحات باستخدام PPR — app/ppr-portal/ — بثلاث صفحات فرعية: نظرة عامة، الطلبات، والتحليلات. تشترك هذه الصفحات في تخطيط ثابت (تنقل علوي + شريط جانبي)، وتحتوي كل صفحة على 2-4 حدود Suspense ديناميكية. أضف Server Actions للسماح للمستخدمين بإرسال البيانات داخل المناطق الديناميكية، مما يؤدي إلى تشغيل revalidateTag() لتحديث المناطق الديناميكية المقابلة.

Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%