React Compiler مع Next.js: دليل التحسين التلقائي الشامل

المتطلبات الأساسية
قبل البدء في هذا الدرس، تأكد من توفر:
- Node.js 20+ مثبّت على جهازك
- مشروع Next.js 15+ (يُفضّل App Router)
- فهم أساسي لـ React hooks وآلية الحفظ (
useMemo،useCallback،React.memo) - محرر أكواد مثل VS Code
ما هو React Compiler؟
React Compiler (المعروف سابقاً بـ React Forget) هو أداة تعمل وقت البناء لتحسين مكونات React تلقائياً عبر إدراج الحفظ (memoization) حيث يلزم. بدلاً من تغليف القيم يدوياً في useMemo والدوال في useCallback والمكونات في React.memo، يقوم المترجم بتحليل الكود وتنفيذ ذلك نيابة عنك.
هذا يعني:
- لا مزيد من الحفظ اليدوي — المترجم يتكفل بذلك
- حجم حزمة أصغر — يتم التخلص من إعادة التصيير غير الضرورية وقت البناء
- أداء أفضل افتراضياً — كل مكون محسّن بدون جهد من المطور
- كود أنظف — إزالة أغلفة
useMemo/useCallbackالزائدة
يفهم React Compiler قواعد React ويحوّل الكود بأمان مع الحفاظ على الصحة.
ما الذي ستبنيه
في هذا الدرس، ستقوم بـ:
- إعداد React Compiler في مشروع Next.js 15
- تكوين إضافة Babel وتكامل ESLint
- فهم ما يحسّنه المترجم وكيف
- تصحيح المخرجات المترجمة للتحقق من التحسينات
- قياس تحسينات الأداء الفعلية
- التعامل مع الحالات الخاصة وسيناريوهات إلغاء الاشتراك
الخطوة 1: إنشاء مشروع Next.js
إذا كان لديك مشروع Next.js 15+ بالفعل، انتقل إلى الخطوة 2. وإلا، أنشئ مشروعاً جديداً:
npx create-next-app@latest react-compiler-demo --typescript --tailwind --app --src-dir
cd react-compiler-demoتحقق من إصدار React:
npm ls reactيجب أن ترى react@19.x.x. يتطلب React Compiler الإصدار 19 أو أحدث.
الخطوة 2: تثبيت React Compiler
ثبّت إضافة Babel وإضافة ESLint للمترجم:
npm install -D babel-plugin-react-compiler eslint-plugin-react-compilerهاتان الحزمتان هما كل ما تحتاجه:
babel-plugin-react-compiler— المترجم الأساسي الذي يحوّل الكود وقت البناءeslint-plugin-react-compiler— يكشف انتهاكات قواعد React التي تمنع الترجمة الآمنة
الخطوة 3: تكوين Next.js
يدعم Next.js 15 React Compiler بشكل مدمج. فعّله في next.config.ts:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
experimental: {
reactCompiler: true,
},
};
export default nextConfig;هذا السطر الواحد كافٍ لتفعيل المترجم لمشروعك بالكامل. يتعامل Next.js مع تكامل إضافة Babel تلقائياً.
التكوين المتقدم
للمزيد من التحكم، يمكنك تمرير خيارات:
const nextConfig: NextConfig = {
experimental: {
reactCompiler: {
compilationMode: "annotation", // ترجمة المكونات المشتركة فقط
panicThreshold: "CRITICAL_ERRORS", // فشل البناء عند الأخطاء الحرجة
},
},
};خيارات compilationMode:
| الوضع | الوصف |
|---|---|
"infer" | الافتراضي. يترجم جميع المكونات والخطافات تلقائياً |
"annotation" | يترجم فقط المكونات/الخطافات التي تحتوي على توجيه "use memo" |
"all" | يترجم كل شيء، حتى لو انتهك قواعد React (استخدم بحذر) |
لمعظم المشاريع، الوضع الافتراضي "infer" هو الخيار الصحيح.
الخطوة 4: إعداد تكامل ESLint
أضف إضافة ESLint لاكتشاف انتهاكات قواعد React مبكراً. حدّث .eslintrc.json:
{
"plugins": ["react-compiler"],
"rules": {
"react-compiler/react-compiler": "error"
}
}أو إذا كنت تستخدم تكوين ESLint المسطح (eslint.config.mjs):
import reactCompiler from "eslint-plugin-react-compiler";
export default [
{
plugins: {
"react-compiler": reactCompiler,
},
rules: {
"react-compiler/react-compiler": "error",
},
},
];شغّل ESLint للتحقق من قاعدة الكود:
npx next lintستظهر أي انتهاكات كأخطاء، تخبرك بالضبط ما يجب إصلاحه قبل أن يتمكن المترجم من تحسين الكود بأمان.
الخطوة 5: فهم ما يتم تحسينه
لنلقِ نظرة على مثال عملي. تأمل هذا المكون قبل React Compiler:
// components/ProductList.tsx — قبل التحسين
"use client";
import { useState } from "react";
interface Product {
id: number;
name: string;
price: number;
category: string;
}
function ProductCard({ product }: { product: Product }) {
console.log(`Rendering ProductCard: ${product.name}`);
return (
<div className="border rounded-lg p-4">
<h3 className="font-bold">{product.name}</h3>
<p className="text-gray-600">${product.price}</p>
</div>
);
}
function CategoryFilter({
categories,
selected,
onSelect,
}: {
categories: string[];
selected: string;
onSelect: (cat: string) => void;
}) {
console.log("Rendering CategoryFilter");
return (
<div className="flex gap-2 mb-4">
{categories.map((cat) => (
<button
key={cat}
onClick={() => onSelect(cat)}
className={selected === cat ? "bg-blue-500 text-white px-3 py-1 rounded" : "bg-gray-200 px-3 py-1 rounded"}
>
{cat}
</button>
))}
</div>
);
}
export default function ProductList({ products }: { products: Product[] }) {
const [selectedCategory, setSelectedCategory] = useState("All");
const [searchQuery, setSearchQuery] = useState("");
const categories = ["All", ...new Set(products.map((p) => p.category))];
const filteredProducts = products.filter((p) => {
const matchesCategory = selectedCategory === "All" || p.category === selectedCategory;
const matchesSearch = p.name.toLowerCase().includes(searchQuery.toLowerCase());
return matchesCategory && matchesSearch;
});
return (
<div>
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search products..."
className="border rounded px-3 py-2 mb-4 w-full"
/>
<CategoryFilter
categories={categories}
selected={selectedCategory}
onSelect={setSelectedCategory}
/>
<div className="grid grid-cols-3 gap-4">
{filteredProducts.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</div>
</div>
);
}المشكلة بدون React Compiler
بدون المترجم، كل ضغطة مفتاح في حقل البحث تسبب:
- إعادة تصيير
ProductList(تغيرت الحالة) - إعادة إنشاء مصفوفة
categories(مرجع جديد كل تصيير) - إعادة تصيير
CategoryFilter(مرجع جديد لخاصيةcategories) - إعادة إنشاء دالة
onSelect(مرجع دالة جديد) - إعادة تصيير جميع مكونات
ProductCard
الإصلاح اليدوي القديم
قبل React Compiler، كنت ستضيف يدوياً:
// حفظ كل شيء يدوياً — مرهق وعرضة للخطأ
const categories = useMemo(
() => ["All", ...new Set(products.map((p) => p.category))],
[products]
);
const filteredProducts = useMemo(
() => products.filter((p) => { /* ... */ }),
[products, selectedCategory, searchQuery]
);
const handleSelect = useCallback(
(cat: string) => setSelectedCategory(cat),
[]
);
// بالإضافة إلى تغليف المكونات الفرعية في React.memo()
const ProductCard = React.memo(function ProductCard({ product }: { product: Product }) {
// ...
});ما يفعله React Compiler تلقائياً
مع تفعيل React Compiler، يتم تحويل الكود النظيف الأصلي (بدون useMemo/useCallback) تلقائياً وقت البناء. المترجم:
- يحفظ حساب
categories - يحفظ فلترة
filteredProducts - يحفظ دالة
onSelect - يخزّن عناصر JSX التي لم تتغير
- يتخطى إعادة تصيير
CategoryFilterعندما يتغيرsearchQueryفقط
أنت تكتب كوداً نظيفاً. المترجم يتولى الأداء.
الخطوة 6: التحقق من مخرجات المترجم
للتأكد من عمل المترجم، ثبّت React DevTools وابحث عن شارة "Memo":
# في متصفحك، ثبّت إضافة React DevTools
# ثم افتح DevTools ← تبويب Componentsالمكونات المحسّنة بواسطة React Compiler تعرض شارة "Memo ✨" في React DevTools. هذا يؤكد أن المترجم قد حفظ هذا المكون تلقائياً.
يمكنك أيضاً التحقق من المخرجات المترجمة مباشرة. أضف هذا إلى next.config.ts للتصحيح:
const nextConfig: NextConfig = {
experimental: {
reactCompiler: {
// تسجيل نتائج الترجمة
logger: {
logEvent(filename, event) {
console.log(`[React Compiler] ${filename}:`, event);
},
},
},
},
};شغّل خادم التطوير وتحقق من مخرجات الطرفية:
npm run devسترى سجلات توضح المكونات التي تمت ترجمتها والتحسينات المطبقة.
الخطوة 7: قياس الأداء
لنقيس التأثير الفعلي. أنشئ مكون اختبار أداء:
// app/perf-test/page.tsx
"use client";
import { useState, useEffect, useRef } from "react";
function ExpensiveChild({ value }: { value: number }) {
// محاكاة تصيير مكلف
const start = performance.now();
while (performance.now() - start < 2) {
// انتظار مشغول لمدة 2 مللي ثانية
}
return <div className="p-2 border rounded">{value}</div>;
}
export default function PerfTest() {
const [count, setCount] = useState(0);
const [unrelated, setUnrelated] = useState(0);
const renderCount = useRef(0);
const startTime = useRef(0);
useEffect(() => {
renderCount.current++;
});
const items = Array.from({ length: 100 }, (_, i) => i);
return (
<div className="p-8">
<h1 className="text-2xl font-bold mb-4">اختبار الأداء</h1>
<div className="flex gap-4 mb-4">
<button
onClick={() => {
startTime.current = performance.now();
setCount((c) => c + 1);
}}
className="bg-blue-500 text-white px-4 py-2 rounded"
>
زيادة المرتبط: {count}
</button>
<button
onClick={() => {
startTime.current = performance.now();
setUnrelated((u) => u + 1);
}}
className="bg-gray-500 text-white px-4 py-2 rounded"
>
زيادة غير المرتبط: {unrelated}
</button>
</div>
<p>عدد مرات التصيير: {renderCount.current}</p>
<div className="grid grid-cols-10 gap-2">
{items.map((i) => (
<ExpensiveChild key={i} value={i + count} />
))}
</div>
</div>
);
}بدون React Compiler: النقر على "زيادة غير المرتبط" يعيد تصيير جميع مكونات ExpensiveChild الـ 100 (تأخير حوالي 200 مللي ثانية).
مع React Compiler: النقر على "زيادة غير المرتبط" يتخطى إعادة تصيير جميع ExpensiveChild لأن خصائصها لم تتغير (استجابة فورية تقريباً).
استخدم تبويب الأداء في Chrome DevTools للتسجيل والمقارنة:
- افتح DevTools، اذهب إلى تبويب Performance
- انقر Record
- انقر "زيادة غير المرتبط" 5 مرات
- أوقف التسجيل
- قارن مخططات اللهب مع وبدون المترجم
الخطوة 8: التعامل مع الحالات الخاصة
إلغاء الاشتراك في الترجمة
إذا لم يعمل مكون بشكل صحيح بعد الترجمة، يمكنك استثناءه:
// أضف توجيه "use no memo" لتخطي الترجمة
"use no memo";
export default function LegacyComponent() {
// لن يتم ترجمة هذا المكون
// مفيد للمكونات التي تعتمد على هوية المرجع
}الاشتراك (وضع التعليق التوضيحي)
إذا كنت تستخدم compilationMode: "annotation"، اشترك في مكونات محددة:
// أضف توجيه "use memo" لتفعيل الترجمة
"use memo";
export default function OptimizedComponent() {
// فقط هذا المكون يتم ترجمته
}الانتهاكات الشائعة
سيتخطى المترجم المكونات التي تنتهك قواعد React. المشكلات الشائعة:
1. تعديل الخصائص أو الحالة مباشرة:
// خطأ — المترجم لا يمكنه تحسين هذا
function BadComponent({ items }: { items: string[] }) {
items.sort(); // تعديل الخصائص!
return <ul>{items.map((item) => <li key={item}>{item}</li>)}</ul>;
}
// صحيح — أنشئ مصفوفة جديدة
function GoodComponent({ items }: { items: string[] }) {
const sorted = [...items].sort();
return <ul>{sorted.map((item) => <li key={item}>{item}</li>)}</ul>;
}2. استدعاء الخطافات بشكل مشروط:
// خطأ — يجب استدعاء الخطافات بشكل غير مشروط
function BadComponent({ show }: { show: boolean }) {
if (show) {
const [value, setValue] = useState(0); // خطاف مشروط!
}
}
// صحيح — استدعِ الخطافات دائماً في المستوى الأعلى
function GoodComponent({ show }: { show: boolean }) {
const [value, setValue] = useState(0);
if (!show) return null;
return <div>{value}</div>;
}3. استخدام المراجع أثناء التصيير:
// خطأ — قراءة المراجع أثناء التصيير تكسر الحفظ
function BadComponent() {
const ref = useRef(0);
ref.current++; // تأثير جانبي أثناء التصيير!
return <div>{ref.current}</div>;
}
// صحيح — استخدم المراجع في التأثيرات أو معالجات الأحداث
function GoodComponent() {
const ref = useRef(0);
useEffect(() => {
ref.current++;
});
return <div>تحقق من الطرفية</div>;
}الخطوة 9: ترحيل مشروع قائم
للمشاريع القائمة، اتبع استراتيجية الترحيل التدريجي:
المرحلة 1: أضف إضافة ESLint أولاً
npm install -D eslint-plugin-react-compilerشغّل المدقق وأصلح جميع الانتهاكات قبل تفعيل المترجم.
المرحلة 2: فعّل في وضع التعليق التوضيحي
// next.config.ts
const nextConfig: NextConfig = {
experimental: {
reactCompiler: {
compilationMode: "annotation",
},
},
};أضف "use memo" لبعض المكونات واختبر بدقة.
المرحلة 3: التبديل إلى وضع الاستنتاج
بمجرد الثقة، بدّل إلى الوضع الافتراضي:
const nextConfig: NextConfig = {
experimental: {
reactCompiler: true,
},
};المرحلة 4: إزالة الحفظ اليدوي
الآن يمكنك إزالة استدعاءات useMemo وuseCallback وReact.memo اليدوية بأمان:
# ابحث عن جميع حالات الحفظ اليدوي في قاعدة الكود
grep -rn "useMemo\|useCallback\|React.memo" --include="*.tsx" --include="*.ts" src/أزلها واحدة تلو الأخرى، مع التحقق من أن المترجم يتعامل مع كل حالة بشكل صحيح.
الخطوة 10: أفضل الممارسات
افعل
- اكتب React نظيف ومعياري — المترجم يكافئ الكود البسيط
- اتبع قواعد React — مكونات نقية، لا تأثيرات جانبية أثناء التصيير
- استخدم إضافة ESLint — اكتشف الانتهاكات قبل وصولها للإنتاج
- اختبر الأداء مع DevTools — تحقق من أن المترجم يساعد
لا تفعل
- لا تحتفظ بالحفظ اليدوي بجانب المترجم — إنه زائد عن الحاجة
- لا تستخدم
"use no memo"كأول حل — أصلح المشكلة الأساسية بدلاً من ذلك - لا تعتمد على هوية المرجع لمنطق العمل — المترجم قد يغير وقت إنشاء الكائنات
- لا تعدّل الكائنات أو المصفوفات في مكانها — أنشئ مراجع جديدة دائماً
مكونات الخادم
يفيد React Compiler بشكل أساسي مكونات العميل ("use client"). مكونات الخادم تُصيَّر مرة واحدة على الخادم، لذا الحفظ له تأثير أقل. ركّز جهود التحسين على الكود التفاعلي من جانب العميل.
استكشاف الأخطاء وإصلاحها
أخطاء البناء بعد تفعيل المترجم
إذا فشل البناء، تحقق من رسالة الخطأ. الأسباب الشائعة:
- بنية غير مدعومة — تأكد من استخدام أنماط React القياسية
- تعارض مكتبة خارجية — بعض المكتبات تستخدم أنماطاً غير قياسية؛ استخدم
"use no memo"على المكونات المغلّفة - إصدار React قديم — حدّث إلى React 19+
المكون يتصرف بشكل مختلف
إذا عمل مكون بشكل مختلف بعد الترجمة:
- أضف
"use no memo"لعزل المشكلة - تحقق مما إذا كان المكون يعتمد على هوية المرجع
- تأكد من اتباعه لقواعد React
- أبلغ عن المشكلة في مستودع React Compiler على GitHub
لا تحسّن في الأداء
إذا لم تلاحظ تحسينات:
- مكوناتك قد تكون فعّالة بالفعل
- قد يكون عنق الزجاجة في الشبكة أو جلب البيانات، وليس التصيير
- استخدم React DevTools Profiler لتحديد عنق الزجاجة الفعلي
الخلاصة
يغيّر React Compiler طريقة كتابة تطبيقات React. بدلاً من تشتيت الكود بـ useMemo وuseCallback وReact.memo، تكتب مكونات نظيفة وبسيطة وتدع المترجم يتولى التحسين تلقائياً.
النقاط الرئيسية:
- سطر واحد في
next.config.tsيفعّل التحسين التلقائي - إضافة ESLint تكتشف المشكلات قبل وصولها للإنتاج
- الكود النظيف يفوز — المترجم يكافئ React المعياري
- الاعتماد التدريجي مدعوم من خلال وضع التعليق التوضيحي
- تحسينات الأداء حقيقية — خاصة للواجهات المعقدة مع إعادة تصيير متكررة
مستقبل أداء React تلقائي. ابدأ باستخدام React Compiler اليوم ودع الكود يكون نظيفاً وسريعاً في آن واحد.
الخطوات التالية
- اقرأ وثائق React Compiler الرسمية لأحدث التحديثات
- استكشف React DevTools Profiler لتحليل الأداء
- اطّلع على درسنا حول React 19 Server Actions لمزيد من ميزات React 19
- تعرّف على Next.js 15 Partial Prerendering لتحسين جانب الخادم
ناقش مشروعك معنا
نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.
دعنا نجد أفضل الحلول لاحتياجاتك.
مقالات ذات صلة

Next.js 15 Partial Prerendering (PPR): بناء لوحة تحكم فائقة السرعة مع العرض الهجين
أتقن Partial Prerendering في Next.js 15 — ادمج العرض الثابت والديناميكي في صفحة واحدة. ابنِ لوحة تحكم تحليلية بقالب ثابت فوري ومحتوى ديناميكي متدفق.

بناء تطبيق كامل يعمل بالوقت الحقيقي باستخدام Convex و Next.js 15
تعلّم كيفية بناء تطبيق كامل يعمل بالوقت الحقيقي باستخدام Convex و Next.js 15. يغطي هذا الدليل تصميم المخططات والاستعلامات والتعديلات والاشتراكات الفورية والمصادقة ورفع الملفات — مع أمان أنواع شامل.

بناء تطبيق كامل مع Firebase و Next.js 15: المصادقة، Firestore والتحديث الفوري
تعلم كيفية بناء تطبيق full-stack مع Next.js 15 و Firebase. يغطي هذا الدليل المصادقة، Firestore، التحديثات الفورية، Server Actions والنشر على Vercel.