{"id":"l-rf-1-2","courseId":"react-fundamentals","lang":"ar","kind":"lesson","summary":"أنشئ مشروع React 19 باستخدام Vite، وشغّل خادم التطوير، وتتبّع كيف ترتبط index.html وmain.jsx وApp.jsx لتعرف أين يذهب كل تعديل.","html":"<p>مشروع React يحتاج إلى أكثر من React. المتصفّحات لا تفهم JSX، وكودك مقسّم إلى عشرات الـ modules، وأنت تريد أن تتحدّث الصفحة لحظة حفظ الملف. أداة البناء تتولّى كل ذلك. في 2026 الخيار الافتراضي لتطبيق React يعمل في المتصفّح هو <strong>Vite</strong>: يبدأ في أقل من ثانية، ويحدّث المتصفّح عند الحفظ، وينتج نسخة صغيرة محسّنة حين تكون جاهزًا للنشر.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">أنشئ المشروع</h2>\n<p>تحقّق أولًا من إصدار Node.js. يحتاج Vite الحالي (7 و8) إلى Node 20.19 أو أحدث، أو 22.12 أو أحدث؛ وعمليًا ثبّت إصدار LTS حاليًا (22 أو 24)، لأن دعم Node 20 انتهى في أبريل 2026.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">node</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -v</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># v24.x or v22.x: a current LTS release</span></span></code></pre></div>\n<p>ثم أنشئ مشروع Watchlist:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">npm</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> create</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> vite@latest</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> watchlist</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --template</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> react</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">cd</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> watchlist</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">npm</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> install</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">npm</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> run</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> dev</span></span></code></pre></div>\n<p>الـ <code dir=\"ltr\">--</code> مهمّ هنا. هو يقول لـ npm «كل ما بعدي يخصّ الأداة التي أشغّلها»، فيصل <code dir=\"ltr\">--template react</code> إلى create-vite بدل أن يبتلعه npm. يعطيك <code dir=\"ltr\">@latest</code> دائمًا أحدث إصدار رئيسي من Vite (وهو Vite 8 وقت كتابة الدرس)، وكل ما في هذه الدورة يعمل بالطريقة نفسها على Vite 7 أو 8. وقد تعرض الإصدارات الحديثة من create-vite أن تثبّت الاعتماديات وتشغّل الخادم عنك؛ لا بأس بالموافقة.</p>\n<p>تطبع الطرفية رابطًا محليًا: <code dir=\"ltr\">http://localhost:5173/</code>. افتحه وسترى صفحة البداية الخاصة بـ Vite + React مع زر عدّاد.</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">نصيحة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>اختيار القالب</span></p>\n<p>القالب <code dir=\"ltr\">react</code> يعطيك JavaScript عادية، وهي ما تستخدمه هذه الدورة لتركّز على React نفسها. والقالب <code dir=\"ltr\">react-ts</code> هو نفسه مع TypeScript، أمّا <code dir=\"ltr\">react-compiler</code> فيضيف React Compiler الاختياري. يمكنك الانتقال إلى TypeScript لاحقًا من دون إعادة كتابة منطق الـ components.</p>\n</aside>\n<h2 id=\"sec-2\" tabindex=\"-1\">كيف ترتبط القطع ببعضها</h2>\n<p>افتح المجلد في محرّرك. معظم الملفات إعدادات لن تلمسها كثيرًا. ثلاثة ملفات فقط تهمّ الآن، وهي تشكّل سلسلة واحدة.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 250\" role=\"img\" aria-labelledby=\"l-rf-1-2-t1\"><defs><marker id=\"arrow-l-rf-1-2-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-rf-1-2-t1\">يحمّل المتصفّح index.html، الذي يحتوي على div جذر ووسم script من نوع module يشير إلى src/main.jsx. يستدعي main.jsx الدالة createRoot على الـ div الجذر ويرسم App من src/App.jsx.</title>\n  <rect class=\"d-box\" x=\"20\" y=\"40\" width=\"190\" height=\"150\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"115\" y=\"70\" text-anchor=\"middle\">index.html</text>\n  <text class=\"d-code\" x=\"115\" y=\"110\" text-anchor=\"middle\">&lt;div id=\"l-rf-1-2-root\"&gt;</text>\n  <text class=\"d-code\" x=\"115\" y=\"140\" text-anchor=\"middle\">&lt;script type=</text>\n  <text class=\"d-code\" x=\"115\" y=\"160\" text-anchor=\"middle\">\"module\" src=…&gt;</text>\n  <rect class=\"d-box-primary\" x=\"250\" y=\"40\" width=\"190\" height=\"150\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"345\" y=\"70\" text-anchor=\"middle\">src/main.jsx</text>\n  <text class=\"d-code\" x=\"345\" y=\"110\" text-anchor=\"middle\">createRoot(root)</text>\n  <text class=\"d-code\" x=\"345\" y=\"140\" text-anchor=\"middle\">.render(&lt;App /&gt;)</text>\n  <rect class=\"d-box-accent\" x=\"480\" y=\"40\" width=\"180\" height=\"150\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"570\" y=\"70\" text-anchor=\"middle\">src/App.jsx</text>\n  <text class=\"d-code\" x=\"570\" y=\"110\" text-anchor=\"middle\">export default</text>\n  <text class=\"d-code\" x=\"570\" y=\"135\" text-anchor=\"middle\">function App()</text>\n  <path class=\"d-arrow\" d=\"M210 150 L248 150\" marker-end=\"url(#arrow-l-rf-1-2-1)\"/>\n  <path class=\"d-arrow\" d=\"M440 120 L478 120\" marker-end=\"url(#arrow-l-rf-1-2-1)\"/>\n  <text class=\"d-label-muted\" x=\"340\" y=\"230\" text-anchor=\"middle\">تحميل ← تركيب ← رسم</text>\n</svg><figcaption>من index.html إلى الـ component المسمّى App</figcaption></figure>\n<p>يقع <strong>index.html</strong> في جذر المشروع، لا داخل مجلد <code dir=\"ltr\">public</code>. يعامله Vite على أنه نقطة الدخول. يحتوي على <code dir=\"ltr\">&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;</code> فارغ ووسم script واحد: <code dir=\"ltr\">&lt;script type=&quot;module&quot; src=&quot;/src/main.jsx&quot;&gt;&lt;/script&gt;</code>.</p>\n<p>أمّا <strong>src/main.jsx</strong> فهو المكان الذي تتسلّم فيه React زمام الأمور:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">src/main.jsx</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { StrictMode } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'react'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { createRoot } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'react-dom/client'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> './index.css'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> App </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> './App.jsx'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">createRoot</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(document.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">getElementById</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'root'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">render</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">StrictMode</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">App</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> /></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">StrictMode</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span></code></pre></div>\n<p>تأخذ <code dir=\"ltr\">createRoot</code> عقدة DOM حقيقية وتُرجع جذرًا تديره React من تلك اللحظة. كل ما داخل <code dir=\"ltr\">#root</code> أصبح ملكًا لـ React؛ لا تغيّره بكود <code dir=\"ltr\">document.querySelector</code> من عندك.</p>\n<p>و<strong>src/App.jsx</strong> هو أول component لك، والمكان الذي ستقضي فيه معظم وقتك. استبدل محتواه بشيء من عندك لتتأكّد أن السلسلة تعمل:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">src/App.jsx</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> default</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> App</span><span style=\"color:#953800;--shiki-dark:#FFA657\">() </span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>My Watchlist&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>احفظ، وسيتحدّث المتصفّح من دون إعادة تحميل. هذا هو الـ hot module replacement (HMR) مع React Fast Refresh، ويجهّزه الـ plugin المسمّى <code dir=\"ltr\">@vitejs/plugin-react</code> في <code dir=\"ltr\">vite.config.js</code>. يمكنك أيضًا حذف <code dir=\"ltr\">src/App.css</code> وإفراغ <code dir=\"ltr\">src/index.css</code>؛ ستنسّق التطبيق كما يجب لاحقًا.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">StrictMode باختصار</h2>\n<p>يضيف <code dir=\"ltr\">&lt;StrictMode&gt;</code> فحوصًا إضافية أثناء التطوير. فهو يرسم كل component مرتين ويشغّل الـ effects مرتين (تركيب، ثم تنظيف، ثم تركيب من جديد) ليكشف الكود الذي لا يحتمل التكرار. إذا رأيت سطرًا يُطبع مرتين أثناء التطوير، فهذا هو السبب. لا يفعل شيئًا في الإنتاج، لذا اتركه مفعّلًا. ستظهر أهميته حين تصل إلى الـ effects في القسم 3.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>فتح index.html مباشرة</span></p>\n<p>النقر المزدوج على <code dir=\"ltr\">index.html</code> يفتحه برابط <code dir=\"ltr\">file://</code>. لن يترجم أحدٌ JSX، وسيفشل تحميل الـ module، وستحصل على صفحة فارغة. افتح دائمًا الرابط الذي يطبعه <code dir=\"ltr\">npm run dev</code>.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">السكربتات الأربعة</h2>\n<p>يعرّف <code dir=\"ltr\">package.json</code> الأوامر التي ستستخدمها:</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"جدول قابل للتمرير\" tabindex=\"0\"><table><thead>\n<tr>\n<th>الأمر</th>\n<th>ماذا يفعل</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code dir=\"ltr\">npm run dev</code></td>\n<td>يشغّل خادم التطوير مع تحديثات فورية</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">npm run build</code></td>\n<td>يجمّع التطبيق ويصغّره داخل <code dir=\"ltr\">dist/</code></td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">npm run preview</code></td>\n<td>يعرض <code dir=\"ltr\">dist/</code> محليًا لتفحص النسخة الحقيقية</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">npm run lint</code></td>\n<td>يشغّل أداة الفحص (linter)، ومنها قواعد الـ hooks</td>\n</tr>\n</tbody>\n</table></div><p>بقية المجلد أدوار مساندة. يسرد <code dir=\"ltr\">package.json</code> اعتمادياتك (<code dir=\"ltr\">react</code> و<code dir=\"ltr\">react-dom</code>) وأدوات التطوير (<code dir=\"ltr\">vite</code> و<code dir=\"ltr\">@vitejs/plugin-react</code> وأداة فحص). و<code dir=\"ltr\">node_modules/</code> هو المكان الذي يضعها فيه <code dir=\"ltr\">npm install</code>؛ حجمه كبير، ويُعاد بناؤه من <code dir=\"ltr\">package.json</code> في أي وقت، ولا يدخل Git أبدًا. ويحتوي <code dir=\"ltr\">public/</code> على ملفات تُقدَّم كما هي من جذر الموقع، مثل الـ favicon. أمّا ملف إعدادات أداة الفحص فيختلف بحسب إصدار القالب: القوالب الحالية تستخدم Oxlint (<code dir=\"ltr\">.oxlintrc.json</code>)، والقوالب الأقدم وخيار ESLint تستخدم <code dir=\"ltr\">eslint.config.js</code>.</p>\n<p>أمّا <code dir=\"ltr\">dist/</code> فمخرجات مولَّدة. لا تعدّل ملفاته أبدًا، ولا تضفه إلى Git؛ ملف <code dir=\"ltr\">.gitignore</code> في المشروع الجاهز يستثنيه أصلًا. وحين تنشر في القسم الأخير، سيشغّل Vercel الأمر <code dir=\"ltr\">npm run build</code> نيابةً عنك.</p>\n<p>مشروعك يعمل الآن، وتعرف أين تُركَّب React. إذا دخل خادم التطوير يومًا في حالة غريبة بعد تثبيت حزمة أو إعادة تسمية ملفات، أوقفه بـ Ctrl+C وشغّل <code dir=\"ltr\">npm run dev</code> من جديد. إعادة التشغيل تستغرق ثانية وتحلّ مشاكل أكثر مما ينبغي. في الدرس التالي تكتب أول component حقيقي لك في <code dir=\"ltr\">App.jsx</code> وتقسّم Watchlist إلى قطع قابلة لإعادة الاستخدام.</p>\n","toc":[{"id":"sec-1","text":"أنشئ المشروع","level":2},{"id":"sec-2","text":"كيف ترتبط القطع ببعضها","level":2},{"id":"sec-3","text":"StrictMode باختصار","level":2},{"id":"sec-4","text":"السكربتات الأربعة","level":2}],"takeaways":["الأمر <code dir=\"ltr\">npm create vite@latest watchlist -- --template react</code> ينشئ مشروع React جاهزًا، و<code dir=\"ltr\">npm run dev</code> يشغّله على localhost:5173 مع تحديث فوري.","يحمّل المتصفّح index.html، الذي يحمّل src/main.jsx، الذي يركّب الـ component المسمّى App داخل الـ div ذي المعرّف root.","الأمر <code dir=\"ltr\">npm run build</code> يكتب موقعًا ثابتًا محسّنًا في dist/، و<code dir=\"ltr\">npm run preview</code> يعرض هذه النسخة محليًا.","يعمل StrictMode على رسم الـ components مرتين وتشغيل الـ effects مرتين أثناء التطوير فقط، ليكشف الأخطاء مبكرًا."],"further":[{"title":"Getting Started (Vite)","url":"https://vite.dev/guide/"},{"title":"Build a React app from Scratch","url":"https://react.dev/learn/build-a-react-app-from-scratch"},{"title":"StrictMode reference","url":"https://react.dev/reference/react/StrictMode"}],"quiz":[{"q":"<p>شغّلت <code dir=\"ltr\">npm create vite@latest watchlist --template react</code> باستخدام npm 10، ومع ذلك طلب منك Vite اختيار إطار عمل. لماذا؟</p>","options":[{"text":"أُزيل القالب <code dir=\"ltr\">react</code> من الإصدارات الحديثة من Vite.","why":"القالب <code dir=\"ltr\">react</code> ما زال موجودًا، إلى جانب <code dir=\"ltr\">react-ts</code> و<code dir=\"ltr\">react-compiler</code> وغيرهما."},{"text":"لا يقبل Vite القوالب إلا إذا كنت داخل مجلد مشروع موجود.","why":"اسم المشروع والقالب يعملان معًا من أي مجلد، وVite ينشئ المجلد الجديد بنفسه."},{"text":"لا يستطيع npm 10 تشغيل أوامر <code dir=\"ltr\">create</code>، فيُسقَط الخيار بصمت.","why":"يشغّل npm أوامر <code dir=\"ltr\">create</code> بلا مشكلة. هو فقط يعامل الخيارات غير المفصولة على أنها خياراته الخاصة."},{"text":"ابتلع npm الخيار <code dir=\"ltr\">--template</code> على أنه خيار خاص به؛ تحتاج إلى <code dir=\"ltr\">--</code> قبله حتى يصل إلى create-vite.","why":"صحيح. مع npm 7 وما بعده، يمرّر الأمر <code dir=\"ltr\">npm create vite@latest watchlist -- --template react</code> كل ما بعد <code dir=\"ltr\">--</code> إلى أداة الإنشاء."}],"answer":3},{"q":"<p>أين تربط React تطبيقك بالصفحة فعليًا في مشروع Vite؟</p>","options":[{"text":"في src/main.jsx، حيث يرسم <code dir=\"ltr\">createRoot(document.getElementById('root'))</code> العنصر <code dir=\"ltr\">&lt;App /&gt;</code>.","why":"صحيح. main.jsx هو الـ module المدخل؛ يجد الـ div الجذر من index.html ويسلّمه إلى React."},{"text":"في index.html، من خلال عنصر مخصّص اسمه <code dir=\"ltr\">&lt;react-app&gt;</code>.","why":"لا يحتوي index.html إلا على <code dir=\"ltr\">&lt;div id=&quot;root&quot;&gt;</code> عادي ووسم script من نوع module. ربط React نفسه يحدث في main.jsx."},{"text":"في vite.config.js، من خلال الـ plugin المسمّى <code dir=\"ltr\">react()</code>.","why":"يعلّم هذا الـ plugin أداة Vite كيف تترجم JSX ويفعّل التحديث السريع، لكنه لا يحدّد أين يُرسم تطبيقك."}],"answer":0},{"q":"<p>أثناء التطوير، يطبع <code dir=\"ltr\">console.log</code> في أعلى الـ component المسمّى App مرتين عند التحميل، بينما يطبع مرة واحدة في نسخة الإنتاج. ما الذي يحدث؟</p>","options":[{"text":"الـ hot module replacement في Vite يحمّل كل module مرتين.","why":"الـ HMR يستبدل الـ modules المتغيّرة بعد التعديل، ولا يشغّل كل component مرتين عند التحميل الأول."},{"text":"في الـ component خطأ يجعله يُرسم في حلقة لا تنتهي.","why":"الحلقة كانت ستطبع أكثر بكثير من مرتين، وغالبًا ترمي الخطأ «Too many re-renders»."},{"text":"يرسم StrictMode الـ components مرتين عمدًا أثناء التطوير ليكشف كود الـ render غير النقي.","why":"صحيح. إنه فحص خاص بالتطوير فقط، ونسخة الإنتاج ترسم مرة واحدة."},{"text":"يرسم React 19 كل component مرتين دائمًا قبل أن يعرض النتيجة.","why":"الرسم المزدوج يحدث فقط تحت StrictMode أثناء التطوير، لا في الإنتاج."}],"answer":2},{"q":"<p>تريد أن ترى بالضبط ما سيحمّله المستخدمون قبل النشر. أيّ تسلسل أوامر يناسب ذلك؟</p>","options":[{"text":"شغّل <code dir=\"ltr\">npm run dev</code> وافتح تبويب Network.","why":"خادم التطوير يقدّم ملفات المصدر غير المجمّعة مع أدوات خاصة بالتطوير، وهذا ليس ما سيُنشر."},{"text":"شغّل <code dir=\"ltr\">npm run build</code> ثم <code dir=\"ltr\">npm run preview</code>.","why":"صحيح. الأمر build يكتب الموقع المحسّن في dist/، والأمر preview يعرض هذا المجلد محليًا لتتنقّل فيه."},{"text":"افتح index.html مباشرة من Finder أو Explorer.","why":"فتح الملف عبر file:// يتجاوز Vite تمامًا؛ لن يُترجم JSX أبدًا وستبقى الصفحة فارغة."}],"answer":1}],"exercise":{"kind":"order","items":["check-node","scaffold","enter-folder","install","start-dev","edit-app"],"title":"جهّز مشروع Watchlist","prompt":"<p>أنت على حاسوب جديد مثبّت عليه Node.js. رتّب هذه الخطوات بالترتيب الذي ستنفّذها به حتى يعمل مشروع Watchlist ويعرض عنوانك الخاص في المتصفّح.</p>\n","hints":["لا يمكنك تثبيت اعتماديات مشروع غير موجود بعد.","أوامر npm تعمل على ملف package.json الموجود في المجلد الحالي، لذا يجب أن تكون داخل المشروع أولًا."],"explanation":"<p>تأكّد أولًا أن إصدار Node حديث بما يكفي لـ Vite (20.19 أو أحدث، أو 22.12 أو أحدث). أمر الإنشاء يصنع المجلد <code dir=\"ltr\">watchlist</code>، لذلك تدخله بـ <code dir=\"ltr\">cd</code> قبل <code dir=\"ltr\">npm install</code>، الذي يقرأ ملف <code dir=\"ltr\">package.json</code> الخاص به. ثم يشغّل <code dir=\"ltr\">npm run dev</code> الخادم على المنفذ 5173. وعندها فقط يظهر تعديل <code dir=\"ltr\">App.jsx</code> في المتصفّح، بفضل الـ hot module replacement.</p>\n","checkLabels":{},"itemText":{"check-node":"<code dir=\"ltr\">node -v</code> — تأكّد أن إصدار Node.js هو 20.19 أو أحدث، أو 22.12 أو أحدث","scaffold":"<code dir=\"ltr\">npm create vite@latest watchlist -- --template react</code>","enter-folder":"<code dir=\"ltr\">cd watchlist</code>","install":"<code dir=\"ltr\">npm install</code>","start-dev":"<code dir=\"ltr\">npm run dev</code>، ثم افتح http://localhost:5173","edit-app":"استبدل محتوى src/App.jsx بالعنوان <code dir=\"ltr\">&lt;h1&gt;My Watchlist&lt;/h1&gt;</code> واحفظ"}},"words":681,"readMinutes":4}