{"id":"l-rf-4-3","courseId":"react-fundamentals","lang":"ar","kind":"lesson","summary":"انشر Watchlist على Vercel من مستودع GitHub أو من Vercel CLI، وافهم نُسخ الإنتاج ونُسخ المعاينة، وتعامل مع متغيّرات البيئة والتوجيه في جهة المتصفّح بأمان.","html":"<p>المشروع الذي لا يعمل إلا على <code dir=\"ltr\">localhost:5173</code> غير مرئي لأي أحد آخر، بمن فيهم من يقرّرون توظيفك. الـ deploy (النشر) يحوّل Watchlist إلى رابط تضعه في سيرتك الذاتية، وترسله إلى صديق، وتفتحه على هاتفك. لتطبيق Vite على Vercel يستغرق ذلك بضع دقائق، وبعد إعداده، كل <code dir=\"ltr\">git push</code> ينشر تلقائيًا.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">ما الذي تنشره</h2>\n<p>يحوّل <code dir=\"ltr\">npm run build</code> مشروعك إلى مجلد من الملفات الثابتة: <code dir=\"ltr\">dist/index.html</code>، وحزمة JavaScript أو اثنتين، وملف CSS، بأسماء ملفات فيها hash مثل <code dir=\"ltr\">index-B3f9xk2a.js</code> لتستطيع المتصفّحات تخزينها مؤقّتًا إلى الأبد. لا يوجد كود خادم. أي استضافة ثابتة تستطيع تقديمه؛ وVercel شائع لأنه يكتشف إطار العمل، ويبني مع كل push، ويعطيك روابط معاينة مجانًا في خطة Hobby.</p>\n<p>قبل النشر، افحص نسخة الإنتاج محليًا:</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\">npm</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> run</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> build</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\"> preview</span></span></code></pre></div>\n<p>يقدّم <code dir=\"ltr\">preview</code> المجلد <code dir=\"ltr\">dist/</code> على <code dir=\"ltr\">http://localhost:4173</code>. إذا انكسر شيء في الإنتاج فقط، مثل مسار ملف خاطئ أو متغيّر بيئة مفقود، فستكتشفه هنا بدل الموقع الحيّ.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">الخيار 1: الاستيراد من GitHub (الموصى به)</h2>\n<ol>\n<li>ارفع مشروعك إلى مستودع على GitHub. إذا عملت commit في درس بناء Watchlist، فأنشئ مستودعًا فارغًا على GitHub واتبع تعليماته لتنفيذ <code dir=\"ltr\">git remote add origin …</code> و<code dir=\"ltr\">git push -u origin main</code>.</li>\n<li>سجّل الدخول إلى Vercel بحساب GitHub واختر <strong>Add New… → Project</strong>.</li>\n<li>استورد المستودع <code dir=\"ltr\">watchlist</code>. يكتشف Vercel <strong>Vite</strong> ويملأ أمر البناء (<code dir=\"ltr\">vite build</code>، وهو ما يشغّله <code dir=\"ltr\">npm run build</code>) ومجلد المخرجات (<code dir=\"ltr\">dist</code>). اتركهما كما هما.</li>\n<li>انقر <strong>Deploy</strong>. في غضون دقيقة تحصل على رابط إنتاج مثل <code dir=\"ltr\">watchlist-yourname.vercel.app</code>.</li>\n</ol>\n<p>من الآن فصاعدًا، المستودع هو من يقود النشر:</p>\n<ul>\n<li>كل push إلى <code dir=\"ltr\">main</code> يُبنى ويُنشر إلى <strong>الإنتاج</strong>.</li>\n<li>كل push إلى branch آخر، وكل pull request، يحصل على <strong>preview deployment</strong> (نسخة معاينة) خاصة به برابط فريد، يُنشر كتعليق على الـ pull request.</li>\n</ul>\n<p>نُسخ المعاينة تغيّر طريقة عملك. تعيد تنسيق شريط الفلترة على branch، وتفتح pull request، وتنقر رابط المعاينة على هاتفك قبل الـ merge. ولا يرى الإنتاج أي عمل غير مكتمل.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 220\" role=\"img\" aria-labelledby=\"l-rf-4-3-t1\"><defs><marker id=\"arrow-l-rf-4-3-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-4-3-t1\">الـ git push إلى GitHub يُطلق بناءً على Vercel يشغّل vite build وينتج dist. الـ push إلى main يذهب إلى رابط الإنتاج، والـ push إلى الـ branches الأخرى يحصل على رابط معاينة.</title>\n  <rect class=\"d-box\" x=\"10\" y=\"80\" width=\"120\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"70\" y=\"113\" text-anchor=\"middle\">git push</text>\n  <rect class=\"d-box\" x=\"165\" y=\"80\" width=\"120\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"225\" y=\"113\" text-anchor=\"middle\">GitHub</text>\n  <rect class=\"d-box-primary\" x=\"320\" y=\"70\" width=\"160\" height=\"76\" rx=\"10\"/>\n  <text class=\"d-label-strong\" x=\"400\" y=\"100\" text-anchor=\"middle\">بناء Vercel</text>\n  <text class=\"d-code\" x=\"400\" y=\"124\" text-anchor=\"middle\">vite build → dist</text>\n  <rect class=\"d-box-success\" x=\"530\" y=\"30\" width=\"160\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"610\" y=\"54\" text-anchor=\"middle\">main ← الإنتاج</text>\n  <text class=\"d-label-muted\" x=\"610\" y=\"74\" text-anchor=\"middle\">رابط .vercel.app</text>\n  <rect class=\"d-box-accent\" x=\"530\" y=\"130\" width=\"160\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"610\" y=\"154\" text-anchor=\"middle\">branch ← معاينة</text>\n  <text class=\"d-label-muted\" x=\"610\" y=\"174\" text-anchor=\"middle\">رابط فريد لكل push</text>\n  <path class=\"d-arrow\" d=\"M130 108 L163 108\" marker-end=\"url(#arrow-l-rf-4-3-1)\"/>\n  <path class=\"d-arrow\" d=\"M285 108 L318 108\" marker-end=\"url(#arrow-l-rf-4-3-1)\"/>\n  <path class=\"d-arrow\" d=\"M480 95 L528 62\" marker-end=\"url(#arrow-l-rf-4-3-1)\"/>\n  <path class=\"d-arrow\" d=\"M480 122 L528 154\" marker-end=\"url(#arrow-l-rf-4-3-1)\"/>\n</svg><figcaption>من git push إلى رابط حيّ</figcaption></figure>\n<h2 id=\"sec-3\" tabindex=\"-1\">الخيار 2: Vercel CLI</h2>\n<p>إن كنت تفضّل النشر من الطرفية، أو لم يكن الكود على GitHub بعد:</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\"> install</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -g</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> vercel</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">vercel</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">          # first run: log in, link the project, deploy a preview</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">vercel</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --prod</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">   # deploy to production</span></span></code></pre></div>\n<p>أول تشغيل لـ <code dir=\"ltr\">vercel</code> يطرح بضعة أسئلة (النطاق، واسم المشروع، والمجلد) ويكتشف Vite بالطريقة نفسها. وينشئ مجلد <code dir=\"ltr\">.vercel</code> يربط مجلدك بالمشروع؛ أبقِه خارج Git. الـ CLI مفيد للتجارب السريعة. أمّا للمشروع الذي ستواصل العمل عليه، فالتكامل مع Git أفضل، لأن النشر يحدث من دون أن يحتاج أحد إلى تذكّر تشغيل أمر.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">متغيّرات البيئة</h2>\n<p>عاجلًا أم آجلًا ستحتاج إلى إعدادات، مثل الرابط الأساسي لـ API. لا يكشف Vite متغيّرات البيئة لكودك إلا إذا بدأت بـ <code dir=\"ltr\">VITE_</code>:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</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\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> apiUrl</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">meta</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.env.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">VITE_API_URL</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span></code></pre></div>\n<p>محليًا، ضعها في ملف <code dir=\"ltr\">.env.local</code> (مستثنى من Git أصلًا في قالب Vite). وعلى Vercel، أضفها من <strong>Project Settings → Environment Variables</strong>، ثم أعد النشر، لأن Vite يضمّن القيم حين يبني.</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>الأسرار في متغيّرات VITE_</span></p>\n<p>تُكتب متغيّرات <code dir=\"ltr\">VITE_</code> داخل حزمة JavaScript كنص عادي. يستطيع أي أحد فتح DevTools وقراءتها. هذا مقبول لرابط API علني، وغير مقبول إطلاقًا لمفتاح API خاص. الأسرار مكانها خادم، مثل serverless function، ولا مكان لها أبدًا في كود المتصفّح.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">التوجيه على استضافة ثابتة</h2>\n<p>لـ Watchlist صفحة واحدة، فكل طلب هو لـ <code dir=\"ltr\">/</code> ويقدّم Vercel الملف <code dir=\"ltr\">index.html</code>. لكن إن أضفت لاحقًا توجيهًا في جهة المتصفّح باستخدام React Router، فإن رابطًا مثل <code dir=\"ltr\">/watched</code> لا يوجد إلا داخل JavaScript الخاص بك. وتحديث الصفحة عليه يطلب من الخادم ملفًا اسمه <code dir=\"ltr\">/watched</code> غير موجود، فتحصل على 404. والإصلاح rewrite يقدّم هيكل التطبيق لأي مسار:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"json\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">vercel.json</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:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  \"rewrites\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: [{ </span><span style=\"color:#116329;--shiki-dark:#7EE787\">\"source\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/(.*)\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#116329;--shiki-dark:#7EE787\">\"destination\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/index.html\"</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\">dist/</code>، مثل ملفات JavaScript وCSS، ما زالت تُقدَّم بشكل طبيعي؛ والـ rewrite لا يُطبَّق إلا حين لا يطابق أي ملف.</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>افتح رابط الإنتاج على هاتفك، وأضف فيلمًا، وحدّث الصفحة، وتأكّد أنه ما زال موجودًا. الـ localStorage خاص بكل موقع، فقائمتك على localhost وقائمتك في الإنتاج منفصلتان. هذا متوقّع، وليس خطأ.</p>\n</aside>\n<p>صار Watchlist حيًّا على الإنترنت. في الدرس الأخير تنظر إلى ما تعلّمته، وتخطّط لما تتعلّمه بعد ذلك.</p>\n","toc":[{"id":"sec-1","text":"ما الذي تنشره","level":2},{"id":"sec-2","text":"الخيار 1: الاستيراد من GitHub (الموصى به)","level":2},{"id":"sec-3","text":"الخيار 2: Vercel CLI","level":2},{"id":"sec-4","text":"متغيّرات البيئة","level":2},{"id":"sec-5","text":"التوجيه على استضافة ثابتة","level":2}],"takeaways":["يُبنى تطبيق Vite إلى ملفات ثابتة في dist/، ويكتشفها Vercel تلقائيًا عبر الإعداد المسبق لـ Vite (<code dir=\"ltr\">vite build</code>، والمخرجات في <code dir=\"ltr\">dist</code>).","استيراد مستودع GitHub يعطيك deployment للإنتاج مع كل push إلى main، ورابط معاينة لكل branch آخر ولكل pull request.","تنشر Vercel CLI من الطرفية؛ فالأمر <code dir=\"ltr\">vercel</code> ينشئ معاينة، و<code dir=\"ltr\">vercel --prod</code> ينشر إلى الإنتاج.","وحدها المتغيّرات التي تبدأ بـ <code dir=\"ltr\">VITE_</code> تصل إلى كود المتصفّح، وهي علنية، فلا تضع فيها أسرارًا أبدًا.","تطبيق الصفحة الواحدة الذي فيه مسارات في جهة المتصفّح يحتاج إلى rewrite نحو index.html؛ أمّا Watchlist، بصفحته الواحدة، فلا يحتاجه."],"further":[{"title":"Vite on Vercel","url":"https://vercel.com/docs/frameworks/frontend/vite"},{"title":"Vercel CLI overview","url":"https://vercel.com/docs/cli"},{"title":"Deploying a Static Site (Vite)","url":"https://vite.dev/guide/static-deploy"},{"title":"Env Variables and Modes (Vite)","url":"https://vite.dev/guide/env-and-mode"}],"quiz":[{"q":"<p>استوردت مستودع Watchlist إلى Vercel. ماذا يشغّل Vercel، وماذا يقدّم للزوّار؟</p>","options":[{"text":"يشغّل <code dir=\"ltr\">npm run dev</code> ويُبقي خادم التطوير يعمل للزوّار.","why":"خادم التطوير للتطوير المحلي فقط. أمّا الإنتاج فيقدّم ملفات ثابتة محسّنة."},{"text":"يثبّت الاعتماديات، ويشغّل البناء (<code dir=\"ltr\">vite build</code>)، ويقدّم الملفات الثابتة في <code dir=\"ltr\">dist/</code>.","why":"صحيح. يعرف الإعداد المسبق لـ Vite أمر البناء ومجلد المخرجات، فتعمل القيم الافتراضية كما هي."},{"text":"يرفع مجلد <code dir=\"ltr\">src/</code> ويترجم المتصفّح الـ JSX.","why":"المتصفّحات لا تستطيع تشغيل JSX. خطوة البناء تترجم كل شيء وتجمّعه قبل تقديم أي شيء."},{"text":"يقدّم <code dir=\"ltr\">index.html</code> من جذر المشروع بلا خطوة بناء.","why":"ملف index.html في الجذر يشير إلى <code dir=\"ltr\">/src/main.jsx</code>، وهذا لا يعمل إلا عبر Vite. ما يُقدَّم هو <code dir=\"ltr\">dist/index.html</code> المبني."}],"answer":1},{"q":"<p>فتحت pull request يعيد تنسيق شريط الفلترة. مع إعداد التكامل مع GitHub، ماذا يحدث؟</p>","options":[{"text":"لا شيء حتى تعمل merge، لأن Vercel لا يبني إلا main.","why":"يبني Vercel كل branch يُرفع. وحده رابط الإنتاج ينتظر main."},{"text":"يتحدّث موقع الإنتاج فورًا بالتغييرات غير المدموجة.","why":"لا يُحدَّث الإنتاج إلا من branch الإنتاج (main)، فلا يصل العمل غير المكتمل إلى المستخدمين الحقيقيين."},{"text":"يطلب منك Vercel تشغيل <code dir=\"ltr\">vercel --prod</code> محليًا.","why":"التكامل مع Git ينشر تلقائيًا؛ والـ CLI بديل، لا خطوة إلزامية."},{"text":"يبني Vercel الـ branch وينشر رابط معاينة فريدًا على الـ pull request، ويترك الإنتاج كما هو.","why":"صحيح. نُسخ المعاينة تتيح لك وللمراجعين تجربة التغيير قبل الـ merge."}],"answer":3},{"q":"<p>يقرأ تطبيقك <code dir=\"ltr\">import.meta.env.VITE_TMDB_KEY</code> لاستدعاء API للأفلام. أيّ عبارة صحيحة؟</p>","options":[{"text":"تُضمَّن القيمة في حزمة JavaScript وقت البناء، فيستطيع أي أحد قراءتها في متصفّحه.","why":"صحيح. متغيّرات VITE_ علنية بطبيعتها. لا تستخدمها إلا لقيم آمنة الكشف، واحتفظ بالأسرار الحقيقية على خادم."},{"text":"تبقى القيمة على خوادم Vercel وتُجلب بأمان وقت التشغيل.","why":"يستبدل Vite التعبير <code dir=\"ltr\">import.meta.env.VITE_…</code> بالقيمة الحرفية أثناء البناء. لا يوجد جلب وقت التشغيل."},{"text":"البادئة اختيارية؛ أي متغيّر بيئة متاح بالشكل <code dir=\"ltr\">import.meta.env.NAME</code>.","why":"لا يكشف Vite لكود المتصفّح إلا المتغيّرات ذات البادئة <code dir=\"ltr\">VITE_</code>، تحديدًا لمنع التسريب غير المقصود."}],"answer":0},{"q":"<p>أضفت React Router ليصبح <code dir=\"ltr\">/watched</code> صفحة مستقلة. التنقّل بالنقر يعمل، لكن تحديث الصفحة على <code dir=\"ltr\">/watched</code> في الإنتاج يعرض 404. ما الإصلاح؟</p>","options":[{"text":"انقل الصفحة إلى مجلد <code dir=\"ltr\">public/watched/</code>.","why":"الصفحة تُرسم بـ JavaScript من index.html؛ والمجلد الثابت سيحتاج إلى نسخته الخاصة من التطبيق كله."},{"text":"غيّر البناء على Vercel إلى <code dir=\"ltr\">npm run dev</code>.","why":"خادم التطوير ليس خادم إنتاج. الإصلاح قاعدة توجيه للملفات الثابتة."},{"text":"أضف rewrite في <code dir=\"ltr\">vercel.json</code> يرسل كل مسار إلى <code dir=\"ltr\">/index.html</code>، فيُحمَّل التطبيق ويتولّى الـ router الرابط.","why":"صحيح. لا يوجد ملف اسمه <code dir=\"ltr\">watched</code> على الخادم؛ والـ rewrite يقدّم هيكل التطبيق لأي مسار ويترك التوجيه في جهة المتصفّح يتولّى الأمر."}],"answer":2}],"exercise":{"kind":"order","items":["build-preview","commit","push","import","deploy","branch-preview","merge"],"title":"انشر Watchlist","prompt":"<p>رتّب هذه الخطوات: من تطبيق يعمل على حاسوبك إلى موقع إنتاج حيّ، ثم نشر تغيير إضافي بأمان عبر نسخة معاينة.</p>\n","hints":["افحص نسخة الإنتاج على جهازك قبل أن يغادره أي شيء.","يستورد Vercel مستودعًا، لذا يجب أن يكون الكود على GitHub أولًا.","التغيير على branch يحصل على رابط معاينة؛ والإنتاج لا يتغيّر إلا حين يتغيّر main."],"explanation":"<p>تتحقّق من البناء الحقيقي محليًا باستخدام <code dir=\"ltr\">npm run build</code> و<code dir=\"ltr\">npm run preview</code>، ثم تعمل commit، وترفع إلى GitHub ليجد Vercel مستودعًا يستورده. الاستيراد يكتشف Vite (<code dir=\"ltr\">vite build</code>، والمخرجات في <code dir=\"ltr\">dist</code>)، وأول نشر يعطيك رابط الإنتاج. بعد ذلك تذهب التغييرات إلى branch: يحصل الـ pull request على رابط معاينة خاص به لتفحصه، والـ merge إلى <code dir=\"ltr\">main</code> يُطلق نشر الإنتاج.</p>\n","checkLabels":{},"itemText":{"build-preview":"شغّل <code dir=\"ltr\">npm run build</code> و<code dir=\"ltr\">npm run preview</code> وجرّب التطبيق محليًا","commit":"اعمل commit للتطبيق العامل باستخدام Git","push":"أنشئ مستودعًا على GitHub ونفّذ <code dir=\"ltr\">git push -u origin main</code>","import":"في Vercel، اختر Add New → Project واستورد المستودع (الإعداد المسبق: Vite، والمخرجات: dist)","deploy":"انقر Deploy وافتح رابط الإنتاج على هاتفك","branch-preview":"أعد تنسيق شريط الفلترة على branch، وافتح pull request وافحص رابط المعاينة الخاص به","merge":"ادمج الـ pull request في main ليتحدّث الإنتاج"}},"words":604,"readMinutes":4}