<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; JavaScript</title><link>https://academy.hsoub.com/programming/javascript/page/4/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; JavaScript</description><language>ar</language><item><title>&#x628;&#x62F;&#x621; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x625;&#x637;&#x627;&#x631; &#x627;&#x644;&#x639;&#x645;&#x644; Svelte &#x644;&#x628;&#x646;&#x627;&#x621; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; &#x648;&#x64A;&#x628;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A8%D8%AF%D8%A1-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-svelte-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D9%88%D9%8A%D8%A8-r1810/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_12/639058c944e64_-----Svelte.png.562a358beff189d884fd818b43bf03bd.png" /></p>

<p>
	سنقدّم في هذا المقال مقدمةً سريعةً عن إطار عمل <a href="https://svelte.dev/" rel="external nofollow">Svelte</a>، إذ سنرى كيفية عمله وما يميزه عن باقي أطر العمل وأدواته، ثم سنتعلم كيفية إعداد بيئة التطوير وإنشاء تطبيق ويب بسيط وفهم بنية المشروع ومعرفة كيفية تشغيله محليًا وإنشائه للإنتاج.
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: يوصَى على الأقل بأن تكون على دراية بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a>، ومعرفة باستخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">سطر الأوامر أو الطرفية</a>، إذ يُعَد إطار العمل Svelte مصرِّفًا Compiler ينشئ شيفرة جافاسكربت مُصغَّرةً ومُحسَّنةً من شيفرتنا البرمجية، لذا ستحتاج إلى طرفية مثبَّت عليها node و npm لتصريف وبناء تطبيقك.
	</li>
	<li>
		<strong>الهدف</strong>: إعداد بيئة تطوير Svelte محلية وإنشاء وبناء تطبيق بسيط وفهم أساسيات كيفية عمله.
	</li>
</ul>
<h2>
	إطار عمل Svelte: طريقة جديدة لبناء واجهات المستخدم
</h2>

<p>
	يوفِّر إطار العمل Svelte نهجًا مختلفًا لبناء تطبيقات الويب عن بعض أطر العمل الأخرى التي تحدثنا عنها في هذه السلسلة <a href="https://academy.hsoub.com/tags/%D8%AA%D8%B9%D9%84%D9%85%20%D8%AA%D8%B7%D9%88%D9%8A%D8%B1%20%D8%A7%D9%84%D9%88%D9%8A%D8%A8/" rel="">تعلم تطوير الويب</a> مثل <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-ember-r1648" rel="">Ember</a> أو <a href="https://academy.hsoub.com/programming/javascript/vuejs/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-vuejs-r1664/" rel="">Vue.js</a>، إذ تطبّق أطر العمل مثل <a href="https://academy.hsoub.com/programming/javascript/react/%D8%A8%D8%AF%D8%A1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-react-r1569/" rel="">React</a> أو <a href="https://academy.hsoub.com/programming/javascript/vuejs/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%A7%D9%84%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-vuejs-r1665/" rel="">Vue.js</a> الجزء الأكبر من عملها في متصفح المستخدِم أثناء تشغيل التطبيق، بينما ينقل إطار Svelte العمل إلى خطوة التصريف التي لا تحدث إلا عند بناء تطبيقك، مما ينتج عنه شيفرة مُحسَّنة باستخدام لغة جافاسكربت الصرفة Vanilla JavaScript، كما ينتج عن هذا النهج حزم تطبيقات أصغر وأداء أفضل، بالإضافة إلى تجربة مطور أسهل للأشخاص الذين لديهم خبرة محدودة في النظام البيئي المجتمعي للأدوات الحديثة.
</p>

<p>
	يلتزم إطار عمل Svelte بنموذج تطوير الويب الكلاسيكي باستخدام اللغات HTML و CSS و JS مع إضافة بعض الامتدادات إلى HTML وجافاسكربت، إذ يمكن القول أنه لديه مفاهيم وأدوات أقل للتعلم من خيارات أطر العمل الأخرى، لكن تتمثل عيوب Svelte الرئيسية الحالية في أنه إطار عمل جديد، وبالتالي فإن نظامه البيئي محدود أكثر من الأطر الأقدم من حيث الأدوات والدعم والإضافات وأنماط الاستخدام الواضحة وما إلى ذلك، كما أنّ هناك فرص عمل أقل متعلقة به، لذلك يجب أن تكون مزاياه كافيةً للاهتمام باستخدامه.
</p>

<p>
	<strong>ملاحظة</strong>: أضاف إطار Svelte مؤخرًا دعم <a href="https://wiki.hsoub.com/TypeScript" rel="external">لغة TypeScript</a> الرسمي، وهو أحد أكثر الميزات المطلوبة.
</p>

<h2>
	حالات الاستخدام
</h2>

<p>
	يمكن استخدام إطار عمل Svelte لتطوير أجزاء صغيرة من واجهة أو تطبيقات كاملة، إذ يمكنك إما البدء من نقطة الصفر والسماح لإطار عمل Svelte بتشغيل واجهة المستخدِم أو يمكنك دمجه مع تطبيق موجود مسبقًا، كما يُعَدّ إطار Svelte مناسبًا لمعالجة المواقف التالية:
</p>

<ul>
<li>
		تطبيقات الويب المخصصة للأجهزة ذات الإمكانات المنخفضة: تتميز التطبيقات المُنشَأة باستخدام إطار عمل Svelte بأحجام حزم أصغر، وهي مثالية للأجهزة ذات اتصالات الشبكة البطيئة وقوة المعالجة المحدودة، إذ يؤدي استخدام شيفرة برمجية أقل إلى استخدام كيلوبايتات أقل لتنزيلها وتحليلها وتنفيذها والاستمرار في التنقل ضمن الذاكرة بسلاسة.
	</li>
	<li>
		الصفحات التفاعلية جدًا أو ذات المؤثرات البصرية المعقدة: إذا أردت بناء مؤثرات البيانات البصرية التي تحتاج لعرض عدد كبير من عناصر <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">نموذج DOM</a>، فستضمن مكاسب الأداء التي تأتي من إطار عمل بدون تكاليف تشغيل إضافية أن تكون تفاعلات المستخدِم ذات استجابة سريعة.
	</li>
	<li>
		تأهيل الأشخاص ذوي المعرفة الأساسية بتطوير الويب: يتمتع إطار Svelte بمنحنى تعليمي سطحي، إذ يمكن لمطوري الويب الذين لديهم معرفة أساسية بلغات HTML و CSS وجافاسكربت استيعاب تفاصيل إطار Svelte بسهولة في وقت قصير والبدء في إنشاء تطبيقات الويب.
	</li>
</ul>
<p>
	يساعد <a href="https://sapper.svelte.dev/" rel="external nofollow">إطار عمل Sapper</a> الذي يعتمد على إطار عمل Svelte في تطوير تطبيقات ذات ميزات متقدمة مثل التصيير من طرف الخادم Server-side Rendering وتقسيم الشيفرة والتوجيه المستند إلى الملفات والدعم دون الاتصال بالإنترنت، وهناك <a href="https://svelte-native.technology/" rel="external nofollow">إطار عمل Svelte Native</a> الذي يتيح بناء تطبيقات هاتف محمول أصيلة Native.
</p>

<h2>
	كيفية عمل إطار عمل Svelte
</h2>

<p>
	يمكن لإطار عمل Svelte توسيع لغات HTML و CSS وجافاسكربت نظرًا لكونه مصرِّفًا، مما يؤدي إلى إنشاء شيفرة جافاسكربت مثالية دون أيّ تكاليف تشغيل إضافية، إذ يوسّع إطار عمل Svelte تقنيات الويب الصرفة بالطرق التالية:
</p>

<ul>
<li>
		يوسّع لغة HTML عن طريق السماح بتعابير جافاسكربت في شيفرة التوصيف وتوفير الموجّهات لاستخدام الشروط والحلقات بطريقة تشبه لغة Handlebars.
	</li>
	<li>
		يوسّع لغة CSS عن طريق إضافة آلية تحديد نطاق، مما يسمح لكل مكوِّن بتحديد تنسيقه الخاص دون التعرض لخطر التعارض مع تنسيق المكونات الأخرى.
	</li>
	<li>
		يوسّع لغة جافاسكربت من خلال إعادة تفسير موجّهات محددة للغة لتحقيق تفاعل حقيقي وتسهيل إدارة حالة المكوِّن.
	</li>
</ul>
<p>
	يتدخل المصرِّف فقط في مواقف محددة للغاية وفي سياق مكونات Svelte، كما تُعَدّ الامتدادات في لغة جافاسكربت قليلةً وتُنتَقى بعناية بهدف عدم تغيير صيغة جافاسكربت أو إبعاد المطورين، إذ ستعمل باستخدام لغة جافاسكربت الصرفة Vanilla JavaScript في أغلب الأحيان.
</p>

<h2>
	الخطوات الأولى لاستخدام إطار Svelte
</h2>

<p>
	لا يمكنك إضافة الوسم <code>&lt;script src="svelte.js"‎&gt;</code> إلى صفحتك واستيرادها إلى تطبيقك فقط، إذ سيتعين عليك إعداد بيئة التطوير للسماح للمصرِّف بتطبيق عمله.
</p>

<h3>
	المتطلبات
</h3>

<p>
	يجب تثبيت <a href="https://academy.hsoub.com/programming/javascript/nodejs/" rel="">Node.js</a> للعمل مع إطار عمل Svelte، إذ يوصَى باستخدام إصدار الدعم طويل الأمد LTS، كما <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-%D9%81%D9%8A-nodejs-r1465/" rel="">يتضمن Node مدير الحزم npm</a> ومشغّل الحزم npx. لاحظ أنه يمكنك استخدام مدير الحزم Yarn بدلًا من npm، لكننا سنفترض أنك تستخدِم npm في هذا المقال، ويمكنك مراجعة <a href="https://academy.hsoub.com/programming/workflow/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1472/" rel="">مقال أساسيات إدارة الحزم</a> لمزيد من المعلومات حول npm وyarn.
</p>

<p>
	إذا استخدَمت نظام ويندوز، فيجب عليك تثبيت بعض البرامج لمنحك التكافؤ مع طرفية نظامَي يونكس Unix أو ماك macOS من أجل استخدام أوامر الطرفية المذكورة في هذا المقال، إذ يُعَدّ كل من Gitbash الذي يأتي على أساس جزء من <a href="https://gitforwindows.org/" rel="external nofollow">مجموعة أدوات git لنظام ويندوز</a> أو <a href="https://docs.microsoft.com/en-us/windows/wsl/about" rel="external nofollow">نظام ويندوز الفرعي لنظام لينكس -WSL اختصارًا-</a> مناسبين، كما يُعَدّ برنامج <a href="https://cmder.net/" rel="external nofollow">Cmder</a> بديلًا آخر جيدًا وكاملًا، ويمكنك مراجعة مقال <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">سطر الأوامر</a> للحصول على مزيد من المعلومات حول هذه الأوامر وأوامر الطرفية.
</p>

<h3>
	إنشاء تطبيق Svelte الأول
</h3>

<p>
	أسهل طريقة لإنشاء قالب تطبيق بسيط هي مجرد تنزيل قالب تطبيق البدء من خلال زيارة صفحة <a href="https://github.com/sveltejs/template" rel="external nofollow">sveltejs/template</a> على GitHub أو يمكنك تجنب الاضطرار إلى تنزيله وفك ضغطه واستخدام أداة <a href="https://github.com/Rich-Harris/degit" rel="external nofollow">degit</a> فقط.
</p>

<p>
	أنشئ قالب تطبيق البدء وشغّل أوامر الطرفية التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8569_9" style="">
 <span class="pln">npx degit sveltejs</span><span class="pun">/</span><span class="pln">template moz</span><span class="pun">-</span><span class="pln">todo</span><span class="pun">-</span><span class="pln">svelte cd moz</span><span class="pun">-</span><span class="pln">todo</span><span class="pun">-</span><span class="pln">svelte npm install npm run dev</span></pre>

<p>
	<strong>ملاحظة</strong>: تتيح degit تنزيل أحدث إصدار من محتويات مستودع Git وفك ضغطه، وهذا أسرع بكثير من استخدام <code>git clone</code> لأنه لن ينزّل كل محفوظات المستودع أو ينشئ نسخةً محليةً كاملةً.
</p>

<p>
	سيصرّف إطار عمل Svelte التطبيق ويبنيه بعد تشغيل الأمر <code>npm run dev</code>، كما سيشغّل خادمًا محليًا على المضيف المحلي <code>localhost:8080</code>، إذ يراقب Svelte تحديثات الملفات، ويعيد تلقائيًا تصريف وتحديث التطبيق نيابةً عنك عند إجراء تغييرات على الملفات المصدرية، ثم سيعرض متصفحك شيئًا يشبه ما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="113457" href="https://academy.hsoub.com/uploads/monthly_2022_12/01_svelte-starter-app.png.489b23ba75b6de8dff4ae53a2d4d68e0.png" rel=""><img alt="تصريف إطار عمل Svelte" class="ipsImage ipsImage_thumbnailed" data-fileid="113457" data-unique="upf5ukrrk" src="https://academy.hsoub.com/uploads/monthly_2022_12/01_svelte-starter-app.thumb.png.71e4e4bd82fde15bc446909c43bbe34f.png" style="width: 620px; height: auto;"></a>
</p>

<h3>
	بنية التطبيق
</h3>

<p>
	يأتي قالب البدء بالبنية التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8569_13" style="">
 <span class="pln">moz</span><span class="pun">-</span><span class="pln">todo</span><span class="pun">-</span><span class="pln">svelte </span><span class="pun">├──</span><span class="pln"> README</span><span class="pun">.</span><span class="pln">md </span><span class="pun">├──</span><span class="pln"> package</span><span class="pun">.</span><span class="pln">json </span><span class="pun">├──</span><span class="pln"> package</span><span class="pun">-</span><span class="pln">lock</span><span class="pun">.</span><span class="pln">json </span><span class="pun">├──</span><span class="pln"> rollup</span><span class="pun">.</span><span class="pln">config</span><span class="pun">.</span><span class="pln">js </span><span class="pun">├──</span><span class="pln"> </span><span class="pun">.</span><span class="pln">gitignore </span><span class="pun">├──</span><span class="pln"> node_modules </span><span class="pun">├──</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="pun">│</span><span class="pln">   </span><span class="pun">├──</span><span class="pln"> favicon</span><span class="pun">.</span><span class="pln">png </span><span class="pun">│</span><span class="pln">   </span><span class="pun">├──</span><span class="pln"> index</span><span class="pun">.</span><span class="pln">html </span><span class="pun">│</span><span class="pln">   </span><span class="pun">├──</span><span class="pln"> global</span><span class="pun">.</span><span class="pln">css </span><span class="pun">│</span><span class="pln">   </span><span class="pun">└──</span><span class="pln"> build </span><span class="pun">│</span><span class="pln">       </span><span class="pun">├──</span><span class="pln"> bundle</span><span class="pun">.</span><span class="pln">css </span><span class="pun">│</span><span class="pln">       </span><span class="pun">├──</span><span class="pln"> bundle</span><span class="pun">.</span><span class="pln">js </span><span class="pun">│</span><span class="pln">       </span><span class="pun">└──</span><span class="pln"> bundle</span><span class="pun">.</span><span class="pln">js</span><span class="pun">.</span><span class="pln">map </span><span class="pun">├──</span><span class="pln"> scripts </span><span class="pun">│</span><span class="pln">   </span><span class="pun">└──</span><span class="pln"> setupTypeScript</span><span class="pun">.</span><span class="pln">js </span><span class="pun">└──</span><span class="pln"> src </span><span class="pun">├──</span><span class="pln"> </span><span class="typ">App</span><span class="pun">.</span><span class="pln">svelte </span><span class="pun">└──</span><span class="pln"> main</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	يتكون من المحتويات التالية:
</p>

<ul>
<li>
		الملفان package.json و package-lock.json: يحتويان على معلومات حول المشروع التي يستخدمها Node.js ومدير الحزم npm لإبقاء المشروع منظمًا، ولا تحتاج إلى فهم هذين الملفين على الإطلاق، لكن إذا أردت معرفة المزيد عنهما، فاطلع على مقال <a href="https://academy.hsoub.com/programming/workflow/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1472/" rel="">أساسيات إدارة الحزم</a>.
	</li>
	<li>
		<code>node_modules</code>: هو المكان الذي تحفظ فيه Node اعتماديات المشروع، ولن تُرسَل هذه الاعتماديات إلى مرحلة الإنتاج، وإنما ستُستخدَم فقط لأغراض التطوير.
	</li>
	<li>
		<code>‎.gitignore</code>: يحدِّد git الملفات أو المجلدات التي يجب تجاهلها من المشروع، وهذا مفيد إذا قررت تضمين تطبيقك في مستودع git.
	</li>
	<li>
		rollup.config.js: يستخدِم إطار عمل Svelte مجمّع الوحدات <a href="https://rollupjs.org/guide/en/" rel="external nofollow">rollup.js</a>، كما يوضّح ملف الإعداد كيفية تجميع وبناء تطبيقك، وإذا فضلت استخدام أداة <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-webpack-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-r866/" rel="">Webpack</a>، فيمكنك إنشاء مشروعك باستخدام الأمر <code>npx degit sveltejs/template-webpack svelte-app</code> بدلًا من ذلك.
	</li>
	<li>
		scripts: يحتوي على سكربتات الإعداد المطلوبة، ويجب أن يحتوي حاليًا على الملف setupTypeScript.js فقط.
		<ul>
<li>
				setupTypeScript.js: يضبط هذا السكربت دعم لغة TypeScript في إطارعمل Svelte.
			</li>
		</ul>
</li>
	<li>
		src: هذا المجلد هو المكان الذي توجد فيه شيفرة تطبيقك البرمجية، أي حيث ستنشئ شيفرة تطبيقك.
		<ul>
<li>
				App.svelte: هو مكوّن المستوى الأعلى لتطبيقك، إذ يصيّر حتى الآن الرسالة "Hello World!‎".
			</li>
			<li>
				main.js: نقطة الدخول إلى التطبيق. ينشئ نسخةً من المكون <code>App</code> ويربطها بجسم صفحة html.
			</li>
		</ul>
</li>
	<li>
		public: يحتوي هذا المجلد على جميع الملفات التي ستُنشَر في مرحلة الإنتاج.
		<ul>
<li>
				favicon.png: الرمز المفضل لتطبيقك، وهو شعار Svelte حاليًا.
			</li>
			<li>
				index.html: الصفحة الرئيسية لتطبيقك، وهي في البداية مجرد صفحة HTML5 فارغة تحمّل ملفات CSS وحزم JS التي ينشئها إطار عمل Svelte.
			</li>
			<li>
				global.css: يحتوي هذا الملف على تنسيقات غير محددة النطاق، وهو ملف CSS الذي سيُطبَّق على التطبيق بأكمله.
			</li>
			<li>
				build: يحتوي هذا المجلد على شيفرة CSS وجافاسكربت الناتجة.
				<ul>
<li>
						bundle.css: ملف CSS الذي أنشأه إطار عمل Svelte من التنسيقات المُعرَّفة لكل مكوّن.
					</li>
					<li>
						bundle.js: ملف جافاسكربت المُصرَّف من كل شيفرة جافسكربت المصدرية.
					</li>
				</ul>
</li>
		</ul>
</li>
</ul>
<h2>
	مكون Svelte الأول
</h2>

<p>
	المكونات هي اللبنات الأساسية لتطبيقات Svelte وتُكتَب في الملفات ذات اللاحقة <code>‎.svelte</code> باستخدام مجموعة شاملة من شيفرة HTML، كما تُعَدّ جميع الأقسام الثلاثة <code>&lt;script&gt;</code> و <code>&lt;style&gt;</code> والتوصيف Markup أقسامًا اختياريةً ويمكن أن تظهر بأيّ ترتيب تريده.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8569_21" style="">
 <span class="tag">&lt;script&gt;</span><span class="pln"> </span><span class="com">// ضع شيفرتك البرمجية هنا</span><span class="pln"> </span><span class="tag">&lt;/script&gt;</span><span class="pln"> </span><span class="tag">&lt;style&gt;</span><span class="pln"> </span><span class="com">/* ضع تنسيقاتك هنا */</span><span class="pln"> </span><span class="tag">&lt;/style&gt;</span><span class="pln"> ‏&lt;-- ضع التوصيف (أي عناصر‫ HTML) هنا --!&gt;‏</span></pre>

<p>
	لنلقِ نظرةً على الملف src/App.svelte المرفق مع قالب البداية، حيث يجب أن ترى شيئًا يشبه ما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8569_25" style="">
 <span class="tag">&lt;script&gt;</span><span class="pln"> </span><span class="kwd">export</span><span class="pln"> let name</span><span class="pun">;</span><span class="pln"> </span><span class="tag">&lt;/script&gt;</span><span class="pln"> </span><span class="tag">&lt;main&gt;</span><span class="pln"> </span><span class="tag">&lt;h1&gt;</span><span class="pln">Hello {name}!</span><span class="tag">&lt;/h1&gt;</span><span class="pln"> </span><span class="tag">&lt;p&gt;</span><span class="pln">Visit the </span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"https://svelte.dev/tutorial"</span><span class="tag">&gt;</span><span class="pln">Svelte tutorial</span><span class="tag">&lt;/a&gt;</span><span class="pln"> to learn how to build Svelte apps.</span><span class="tag">&lt;/p&gt;</span><span class="pln"> </span><span class="tag">&lt;/main&gt;</span><span class="pln"> </span><span class="tag">&lt;style&gt;</span><span class="pln"> main </span><span class="pun">{</span><span class="pln"> text</span><span class="pun">-</span><span class="pln">align</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln"> padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1em</span><span class="pun">;</span><span class="pln"> max</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">240px</span><span class="pun">;</span><span class="pln"> margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="kwd">auto</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> h1 </span><span class="pun">{</span><span class="pln"> color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#ff3e00;</span><span class="pln"> text</span><span class="pun">-</span><span class="pln">transform</span><span class="pun">:</span><span class="pln"> uppercase</span><span class="pun">;</span><span class="pln"> font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4em</span><span class="pun">;</span><span class="pln"> font</span><span class="pun">-</span><span class="pln">weight</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="lit">@media</span><span class="pln"> </span><span class="pun">(</span><span class="pln">min</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">640px</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> main </span><span class="pun">{</span><span class="pln"> max</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="tag">&lt;/style&gt;</span></pre>

<h3>
	القسم <code>&lt;script&gt;</code>
</h3>

<p>
	تحتوي كتلة <code>&lt;script&gt;</code> على شيفرة جافاسكربت التي تُشغَّل عند إنشاء نسخة من المكوِّن، إذ تكون المتغيرات المصرَّح عنها أو المستوردة في المستوى الأعلى مرئيةً من شيفرة توصيف المكوِّن، وتُعَدّ متغيرات المستوى الأعلى الطريقة التي يتعامل بها إطار عمل Svelte مع حالة المكوِّن وتكون تفاعليةً افتراضيًا، إذ سنشرح بالتفصيل ما يعنيه ذلك لاحقًا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8569_27" style="">
 <span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">export</span><span class="pln"> let name</span><span class="pun">;</span><span class="pln"> </span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	يستخدِم إطار العمل Svelte الكلمة <code>export</code> للتصريح عن المتغير بوصفه خاصيةً Prop، مما يعني أنه يصبح بإمكان مستخدِمي المكوِّن -مثل المكونات الأخرى- الوصول إليه، ويمثل هذا المثال توسعة إطار عمل Svelte لصيغة لغة جافاسكربت لجعلها أكثر فائدةً مع بقائها مألوفة.
</p>

<h3>
	قسم التوصيف
</h3>

<p>
	يمكنك إدراج أيّ شيفرة HTML تريدها في قسم التوصيف، كما يمكنك إدراج تعبير جافاسكربت صالح ضمن أقواس معقوصة مفردة <code>{}</code>، وسنضمّن في حالتنا قيمة الخاصية <code>name</code> بعد النص <code>Hello</code> مباشرةً.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8569_31" style="">
 <span class="tag">&lt;main&gt;</span><span class="pln"> </span><span class="tag">&lt;h1&gt;</span><span class="pln">Hello {name}!</span><span class="tag">&lt;/h1&gt;</span><span class="pln"> </span><span class="tag">&lt;p&gt;</span><span class="pln">Visit the </span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"https://svelte.dev/tutorial"</span><span class="tag">&gt;</span><span class="pln">Svelte tutorial</span><span class="tag">&lt;/a&gt;</span><span class="pln"> to learn how to build Svelte apps.</span><span class="tag">&lt;/p&gt;</span><span class="pln"> </span><span class="tag">&lt;/main&gt;</span></pre>

<p>
	كما يدعم إطار عمل Svelte وسومًا مثل <code>{‎#if...‎}</code> و <code>{‎#each...‎}</code> و <code>{‎#await...‎}</code> التي تتيح لك تصييرًا مشروطًا لجزء من شيفرة التوصيف والتكرار على قائمة من العناصر والعمل بقيم غير متزامنة.
</p>

<h3>
	القسم <code>&lt;style&gt;</code>
</h3>

<p>
	إذا كانت لديك خبرة في العمل مع لغة CSS، فيجب أن يكون جزء الشيفرة التالية مفهومًا:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_8569_33" style="">
<span class="tag">&lt;style&gt;</span><span class="pln">
  main </span><span class="pun">{</span><span class="pln">
    text</span><span class="pun">-</span><span class="pln">align</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
    padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1em</span><span class="pun">;</span><span class="pln">
    max</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">240px</span><span class="pun">;</span><span class="pln">
    margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="kwd">auto</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  h1 </span><span class="pun">{</span><span class="pln">
    color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#ff3e00;</span><span class="pln">
    text</span><span class="pun">-</span><span class="pln">transform</span><span class="pun">:</span><span class="pln"> uppercase</span><span class="pun">;</span><span class="pln">
    font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4em</span><span class="pun">;</span><span class="pln">
    font</span><span class="pun">-</span><span class="pln">weight</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="lit">@media</span><span class="pln"> </span><span class="pun">(</span><span class="pln">min</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">640px</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    main </span><span class="pun">{</span><span class="pln">
      max</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="tag">&lt;/style&gt;</span></pre>

<p>
	سنطبِّق تنسيقًا على العنصر <code><a href="https://wiki.hsoub.com/HTML/h1-h6" rel="external">&lt;h1&gt;</a></code>، لذلك لا بدّ أنك تتساءل عمّا سيحدث للمكونات الأخرى التي تحتوي على عناصر <code>&lt;h1&gt;</code> ضمنها.
</p>

<p>
	يُحدَّد في إطار عمل Svelte نطاق شيفرة CSS ضمن كتلة <code>&lt;style&gt;</code> الخاصة بمكوِّن ما لهذا المكوِّن فقط من خلال إضافة صنف Class إلى العناصر المحدَّدة، ولا يضاف هذا الصنف عشوائيًا، وإنما يعتمد على قيمة مُعمَّاة Hash خاصة بتنسيق هذا المكوِّن.
</p>

<p>
	يمكنك رؤية جميع هذه الأمور عمليًا من خلال فتح المضيف المحلي <code>localhost:5042</code> في تبويب متصفح جديد، ثم الضغط بزر الفأرة الأيمن أو الضغط على مفتاح <code>Ctrl</code> على العنوان HELLO WORLD!‎ وتحديد الخيار فحص Inspect:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="113458" href="https://academy.hsoub.com/uploads/monthly_2022_12/02_svelte-component-scoped-styles.png.47dbc398e68fa8764a57e588484f9b1b.png" rel=""><img alt="خيار فحص Inspect" class="ipsImage ipsImage_thumbnailed" data-fileid="113458" data-unique="5wen90n9u" src="https://academy.hsoub.com/uploads/monthly_2022_12/02_svelte-component-scoped-styles.thumb.png.0c7b17e97b1daad5737b63bdbe89fae5.png" style="width: 700px; height: auto;"></a>
</p>

<p>
	يغيِّر إطار العمل Svelte عند تصريف التطبيق تعريف تنسيق العنصر <code>h1</code> إلى <code>h1.svelte-1tky8bj</code>، ثم يعدِّل كل عنصر <code>&lt;h1&gt;</code> في المكوِّن إلى الشكل <code>&lt;h1 class="svelte-1tky8bj"‎&gt;</code> بحيث يُطبَّق التنسيق على العنصر الخاص بالمكوِّن المحدَّد فقط.
</p>

<p>
	<strong>ملاحظة</strong>: يمكنك تغيير هذا السلوك وتطبيق التنسيق على محدد Selector بطريقة عامة باستخدام المعدِّل <code>‎:global(...)‎</code>.
</p>

<h2>
	إجراء بعض التغييرات
</h2>

<p>
	يمكنك تعديل المكوِّن <code>App.svelte</code> مثل تعديل العنصر <code>&lt;h1&gt;</code> في السطر رقم 6 من المكوِّن <code>App.svelte</code> بحيث يكون كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8569_37" style="">
<span class="tag">&lt;h1&gt;</span><span class="pln">Hello {name} from MDN!</span><span class="tag">&lt;/h1&gt;</span></pre>

<p>
	يؤدي حفظ التعديلات إلى حفظ التطبيق المُشغَّل على المضيف المحلي <code>localhost:5042</code> تلقائيًا.
</p>

<h3>
	التفاعل في إطار العمل Svelte
</h3>

<p>
	يعني التفاعل Reactivity في سياق إطار عمل واجهة المستخدِم أنّ إطار العمل يمكنه تلقائيًا تحديث نموذج DOM عند تعديل حالة أيّ مكون، إذ يُشغَّل التفاعل في إطار عمل Svelte عن طريق إسناد قيمة جديدة لأيّ متغير في المستوى الأعلى ضمن أحد المكونات، فيمكننا مثلًا تضمين دالة <code>toggleName()‎</code> في المكوِّن <code>App</code> وزر لتشغيلها.
</p>

<p>
	عدّل القسمين <code>&lt;script&gt;</code> والتوصيف كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8569_39" style="">
<span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="kwd">export</span><span class="pln"> let name</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">function</span><span class="pln"> toggleName</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">name </span><span class="pun">===</span><span class="pln"> </span><span class="str">'world'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'svelte'</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'world'</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">main</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="typ">Hello</span><span class="pln"> </span><span class="pun">{</span><span class="pln">name</span><span class="pun">}!&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">button on</span><span class="pun">:</span><span class="pln">click</span><span class="pun">={</span><span class="pln">toggleName</span><span class="pun">}&gt;</span><span class="typ">Toggle</span><span class="pln"> name</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">p</span><span class="pun">&gt;</span><span class="typ">Visit</span><span class="pln"> the </span><span class="pun">&lt;</span><span class="pln">a href</span><span class="pun">=</span><span class="str">"https://svelte.dev/tutorial"</span><span class="pun">&gt;</span><span class="typ">Svelte</span><span class="pln"> tutorial</span><span class="pun">&lt;</span><span class="str">/a&gt; to learn how to build Svelte apps.&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">main</span><span class="pun">&gt;</span></pre>

<p>
	ينفّذ إطار العمل Svelte الدالة <code>toggleName()‎</code> عند النقر على الزر، مما يؤدي إلى تحديث قيمة المتغير <code>name</code>.
</p>

<p>
	يُحدَّث عنوان Label العنصر <code>&lt;h1&gt;</code> تلقائيًا، إذ ينشئ إطار Svelte شيفرة جافاسكربت لتحديث نموذج DOM كلما تغيرت قيمة المتغير <code>name</code> دون استخدام نموذج DOM الافتراضي أو أيّ آلية توافق معقدة أخرى، ولاحظ استخدام <code>:</code> في <code>on:click</code> التي تُعَدّ صيغة Svelte للاستماع إلى أحداث DOM.
</p>

<h2>
	فحص main.js‎: نقطة الدخول إلى التطبيق
</h2>

<p>
	افتح الملف src/main.js حيث يُستورَد ويُستخدَم المكوِّن <code>App</code>، إذ يُعَدّ هذا الملف نقطة الدخول لتطبيقنا، ويبدو في البداية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8569_41" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> from </span><span class="str">'./App.svelte'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> app </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">App</span><span class="pun">({</span><span class="pln">
  target</span><span class="pun">:</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">,</span><span class="pln">
  props</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'world'</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> app</span><span class="pun">;</span></pre>

<p>
	يبدأ الملف main.js باستيراد مكوِّن Svelte الذي سنستخدِمه، ثم ينشئ نسخةً منه في السطر رقم 3، ويمرّر كائنًا له الخاصيات التالية:
</p>

<ul>
<li>
		<code>target</code>: عنصر DOM الذي نريد تصيير المكوِّن ضمنه، وهو العنصر <code>&lt;body&gt;</code> في هذه الحالة.
	</li>
	<li>
		<code>props</code>: القيم المراد إسنادها لكل خاصية للمكوِّن <code>App</code>.
	</li>
</ul>
<h2>
	نظرة إلى خلفية إطار Svelte
</h2>

<p>
	لا بدّ أنك تتساءل عن كيفية تمكّن إطار Svelte من جعل كل هذه الملفات تعمل مع بعضها البعض بطريقة صحيحة، إذ يعالِج مصرّف Svelte القسم <code>&lt;style&gt;</code> لكل مكوِّن ويصرّفه في الملف public/build/bundle.css، ويصرّف قسمَي التوصيف و<code>&lt;script&gt;</code> لكل مكوِّن ويخزن النتيجة في الملف public/build/bundle.js، كما يضيف الشيفرة البرمجية في الملف src/main.js للإشارة إلى ميزات كل مكوِّن
</p>

<p>
	يتضمن الملف public/index.html الملفين bundle.css و bundle.js:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8569_43" style="">
<span class="dec">&lt;!DOCTYPE html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;head&gt;</span><span class="pln">
  </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">'utf-8'</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">'viewport'</span><span class="pln"> </span><span class="atn">content</span><span class="pun">=</span><span class="atv">'width=device-width,initial-scale=1'</span><span class="tag">&gt;</span><span class="pln">

  </span><span class="tag">&lt;title&gt;</span><span class="pln">Svelte app</span><span class="tag">&lt;/title&gt;</span><span class="pln">

  </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">'icon'</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">'image/png'</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">'/favicon.png'</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">'stylesheet'</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">'/global.css'</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">'stylesheet'</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">'/build/bundle.css'</span><span class="tag">&gt;</span><span class="pln">

  </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">defer</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">'/build/bundle.js'</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">

</span><span class="tag">&lt;body&gt;</span><span class="pln">
</span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	حجم الإصدار المصغر من الملف bundle.js أكثر بقليل من 3 كيلوبايت، والذي يتضمن "وقت تشغيل Svelte" -أي 300 سطر فقط من شيفرة جافاسكربت- والمكوِّن المُصرَّف <code>App.svelte</code>، كما يُعَدّ الملف bundle.js ملف جافاسكربت الوحيد الذي يشير إليه الملف index.html، ولا توجد مكتبات أخرى مُحمَّلة في صفحة الويب.
</p>

<p>
	تُعَدّ هذه المساحة أصغر بكثير من الحزم المُصرَّفة في أطر عمل أخرى، وضَع في الحسبان أنه لا يقتصر الأمر على حجم الملفات التي يجب تنزيلها في حالة تجميع الشيفرة البرمجية، والتي هي شيفرة قابلة للتنفيذ يجب تحليلها وتنفيذها والاحتفاظ بها في الذاكرة، لذلك يحدث ذلك فرقًا حقًا خاصةً في الأجهزة ذات الإمكانات المنخفضة أو التطبيقات ذات الاستخدام الكبير <a href="https://academy.hsoub.com/certificates/comptia/%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B1%D9%83%D8%B2%D9%8A%D8%A9-r58/" rel="">لوحدة المعالجة المركزية</a>.
</p>

<h2>
	متابعة هذه السلسلة من المقالات
</h2>

<p>
	سنبني في هذه السلسلة من المقالات تطبيق ويب كامل، لذلك وفّرنا مستودع GitHub مع مجلد يحتوي على شيفرة التطبيق البرمجية الكاملة، كما يمكنك قراءة المحتوى فقط للحصول على فهم جيد لميزات Svelte، ولكن ستحصل على أقصى استفادة من هذا السلسلة من المقالات إذا اتبعت شيفرة التطبيق معنا، كما سنوفر مستودع GitHub مع مجلد يحتوي على شيفرة التطبيق البرمجية كما هي في بداية كل مقال لتسهيل متابعته.
</p>

<p>
	يوفِّر إطار Svelte أداة REPL على الإنترنت، وهي أداة لتطبيقات Svelte ذات الشيفرة الحية المباشرة على الويب دون الحاجة إلى تثبيت أيّ شيء على جهازك، ولنتحدث الآن عن كيفية استخدام هذه الأدوات.
</p>

<h3>
	استخدام Git
</h3>

<p>
	يُعَدّ Git أكثر أنظمة التحكم في الإصدارات شيوعًا مع GitHub، وهو موقع يوفِّر استضافةً لمستودعاتك والعديد من الأدوات للعمل بها، وسنستخدِم GitHub لتتمكّن من تنزيل الشيفرة البرمجية بسهولة.
</p>

<p>
	يجب تنفيذ الأمرالتالي بعد <a href="https://git-scm.com/downloads" rel="external nofollow">تثبيت Git</a> لنسخ المستودع:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8569_46" style="">
<span class="pln">git clone https</span><span class="pun">:</span><span class="com">//github.com/opensas/mdn-svelte-tutorial.git</span></pre>

<p>
	يمكنك إدخال الأمر <code>cd</code> في المجلد المقابل وبدء تشغيل التطبيق في وضع التطوير dev لترى ما يجب أن تكون عليه حالة التطبيق الحالية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8569_48" style="">
<span class="pln">cd </span><span class="lit">02</span><span class="pun">-</span><span class="pln">starting</span><span class="pun">-</span><span class="pln">our</span><span class="pun">-</span><span class="pln">todo</span><span class="pun">-</span><span class="pln">app
npm install
npm run dev</span></pre>

<p>
	<strong>ملاحظة</strong>: إذا أردت تنزيل الملفات فقط دون نسخ مستودع Git، فيمكنك استخدام الأداة degit في الأمر <code>npx degit opensas/mdn-svelte-tutorial</code>، كما يمكنك تنزيل مجلد محدد باستخدام الأمر <code>npx degit opensas/mdn-svelte-tutorial/01-getting-started</code>، ولن تنشئ الأداة degit مستودع git محلي، وإنما ستنزّل ملفات المجلد المحدَّد فقط.
</p>

<h3>
	استخدام أداة REPL في إطار عمل Svelte
</h3>

<p>
	تُعَدّ أداة REPL (أي حلقة قراءة-تقييم-طباعة read–eval–print Loop) بيئةً تفاعليةً تسمح بإدخال الأوامر والاطلاع على النتائج مباشرةً، وتوفِّر العديد من لغات البرمجة أداة REPL، كما تُعَدّ حلقة REPL في إطار Svelte أداةً عبر الإنترنت تتيح إنشاء تطبيقات كاملة وحفظها عبر الإنترنت ومشاركتها مع الآخرين، إذ تُعَدّ أسهل طريقة لبدء العمل باستخدام Svelte من أيّ جهاز دون الحاجة إلى تثبيت أيّ شيء، كما يستخدمها مجتمع Svelte على نطاق واسع، لذا إذا أردت مشاركة فكرة أو طلب المساعدة أو الإبلاغ عن مشكلة، فيمكنك إنشاء نسخة REPL توضِّح المشكلة.
</p>

<p>
	لنلقِ نظرةً سريعةً على أداة REPL في إطار عمل Svelte وكيفية استخدامها، حيث تبدو كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="113459" href="https://academy.hsoub.com/uploads/monthly_2022_12/03_svelte-repl-in-action.png.53209c238df2134f0556c67ef069d650.png" rel=""><img alt="أداة REPL في إطار عمل Svelte" class="ipsImage ipsImage_thumbnailed" data-fileid="113459" data-unique="3lqeetnmp" src="https://academy.hsoub.com/uploads/monthly_2022_12/03_svelte-repl-in-action.thumb.png.9c661664093e4bbffc7420303668e2cd.png" style="width: 750px; height: auto;"></a>
</p>

<p>
	افتح المتصفح وانتقل إلى <a href="https://svelte.dev/repl" rel="external nofollow">أداة REPL</a>. لنتعرف على محتوياتها:
</p>

<ul>
<li>
		سترى شيفرة مكوناتك على الجانب الأيسر من الشاشة، وسترى على اليمين خرج تنفيذ تطبيقك.
	</li>
	<li>
		يتيح لك الشريط الموجود أعلى الشيفرة إنشاء ملفات <code>‎.svelte</code> و <code>‎.js</code> وإعادة تنظيمها، كما يمكنك إنشاء ملف ضمن مجلد من خلال تحديد اسم المسار الكامل components/MyComponent.svelte ثم سيُنشَأ المجلد تلقائيًا.
	</li>
	<li>
		يوجد عنوان أداة REPL فوق هذا الشريط. يمكنك الضغط عليه لتعديله.
	</li>
	<li>
		يوجد على الجانب الأيمن ثلاث تبويبات هي:
		<ul>
<li>
				يعرض تبويب "النتيجة Result" خرج التطبيق، ويوفِّر طرفيةً Console في الأسفل.
			</li>
			<li>
				يتيح تبويب "JS output" فحص شيفرة جافاسكربت التي أنشأها إطار عمل Svelte ويضبط خيارات المصرّف Compiler.
			</li>
			<li>
				يعرض تبويب '"CSS Output" شيفرة CSS التي أنشأها إطار عمل Svelte.
			</li>
		</ul>
</li>
	<li>
		ستجد شريط أدوات فوق التبويبات، حيث يتيح شريط الأدوات الدخول إلى وضع ملء الشاشة وتنزيل تطبيقك، فإذا سجّلتَ الدخول باستخدام حساب GitHub، فستتمكّن من نسخ التطبيق وحفظه، وستتمكن من رؤية جميع أدوات REPL المحفوظة من خلال النقر على اسم مستخدِم حسابك على GitHub وتحديد التطبيقات المحفوظة.
	</li>
</ul>
<p>
	كلما عدّلت أيّ ملف على REPL، فسيعيد إطار عمل Svelte تصريف التطبيق وتحديث تبويب النتيجة، كما يمكنك مشاركة تطبيقك من خلال مشاركة عنوان URL مثل <a href="https://svelte.dev/repl/378dd79e0dfe4486a8f10823f3813190?version=3.23.2" rel="external nofollow">رابط REPL الخاص بتشغيل تطبيقنا الكامل</a>.
</p>

<p>
	<strong>ملاحظة</strong>: لاحظ كيف يمكنك تحديد إصدار Svelte في عنوان URL، إذ يكون ذلك مفيدًا عند الإبلاغ عن المشاكل المتعلقة بإصدار معيّن من إطار Svelte.
</p>

<p>
	<strong>ملاحظة</strong>: لا يمكن لأداة REPL حاليًا التعامل مع أسماء المجلدات بطريقة صحيحة، فإذا أردت متابعة الخطوات التي نطبّقها، فأنشئ جميع مكوناتك ضمن المجلد الجذر، فإذا رأيت مسارًا في الشيفرة <code>import Todos from './components/Todos.svelte'‎</code>، فضَع مكانه عنوان URL مسطح Flat مثل <code>import Todos from './Todos.svelte'‎</code>.
</p>

<p>
	يمكنك نسخ مستودع github -إذا لم تفعل ذلك مسبقًا- باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8569_51" style="">
<span class="pln">git clone https</span><span class="pun">:</span><span class="com">//github.com/opensas/mdn-svelte-tutorial.git</span></pre>

<p>
	ثم يمكنك الوصول إلى حالة التطبيق الحالية من خلال تشغيل الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8569_53" style="">
<span class="pln">cd mdn</span><span class="pun">-</span><span class="pln">svelte</span><span class="pun">-</span><span class="pln">tutorial</span><span class="pun">/</span><span class="lit">01</span><span class="pun">-</span><span class="pln">getting</span><span class="pun">-</span><span class="pln">started</span></pre>

<p>
	أو يمكنك تنزيل محتوى المجلد مباشرةً كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8569_55" style="">
<span class="pln">npx degit opensas</span><span class="pun">/</span><span class="pln">mdn</span><span class="pun">-</span><span class="pln">svelte</span><span class="pun">-</span><span class="pln">tutorial</span><span class="pun">/</span><span class="lit">01</span><span class="pun">-</span><span class="pln">getting</span><span class="pun">-</span><span class="pln">started</span></pre>

<p>
	تذكَّر تشغيل الأمر <code>npm install &amp;&amp; npm run dev</code> لبدء تطبيقك في وضع التطوير، فإذا أردت متابعتنا، فابدأ بكتابة الشيفرة باستخدام الأداة REPL من <a href="https://svelte.dev/repl/fc68b4f059d34b9c84fa042d1cce586c?version=3.23.2" rel="external nofollow">هنا</a>.
</p>

<h2>
	الخلاصة
</h2>

<p>
	ألقينا في هذا المقال النظرة الأولية إلى إطار عمل Svelte بما في ذلك كيفية تثبيته محليًا وإنشاء تطبيق بدء بسيط وكيفية عمل الأساسيات، كما سنبدأ في المقال التالي ببناء أول تطبيق وهو تطبيق قائمة المهام، ولنلخص بعض الأشياء التي تعلمناها في Svelte وهي:
</p>

<ul>
<li>
		تعريف سكربت وتنسيق وتوصيف كل مكون في ملف <code>‎.svelte</code> واحد.
	</li>
	<li>
		يُصرَّح عن خاصيات المكونات باستخدام الكلمة <code>export</code>.
	</li>
	<li>
		يمكن استخدام مكونات Svelte فقط عن طريق استيراد ملف <code>‎.svelte</code> المقابل.
	</li>
	<li>
		يجب تحديد نطاق تنسيق المكونات، مما يمنعها من التضارب مع بعضها البعض.
	</li>
	<li>
		يمكنك تضمين أيّ تعبير جافاسكربت في قسم التوصيف Markup بوضع هذا التعبير بين قوسين معقوصين.
	</li>
	<li>
		تشكّل متغيرات المستوى الأعلى للمكوِّن حالته.
	</li>
	<li>
		يمكن إطلاق التفاعل عن طريق إسناد قيمة جديدة لمتغير المستوى الأعلى.
	</li>
</ul>
<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_getting_started" rel="external nofollow">Getting started with Svelte</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/python/flask/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D8%B7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%81%D9%8A-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%81%D9%84%D8%A7%D8%B3%D9%83-%D9%86%D9%85%D9%88%D8%B0%D8%AC%D8%A7-r1547/" rel="">استخدام أطر العمل في برمجة تطبيقات الويب: فلاسك نموذجا</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-r1409/" rel="">مقدمة في بناء تطبيقات الويب باستخدام إطار العمل Angular وقاعدة بيانات Firestore</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/laravel/%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d8%a7%d8%aa-%d8%a8%d9%86%d8%a7%d8%a1-%d8%a7%d9%84%d8%aa%d8%b7%d8%a8%d9%8a%d9%82%d8%a7%d8%aa-%d9%81%d9%8a-%d8%a5%d8%b7%d8%a7%d8%b1-%d8%a7%d9%84%d8%b9%d9%85%d9%84-laravel-5-r216/" rel="">أساسيات بناء التطبيقات في إطار العمل Laravel 5</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/django/%D8%A7%D9%84%D8%A8%D8%AF%D8%A1-%D9%85%D8%B9-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D8%AC%D8%A7%D9%86%D8%BA%D9%88-%D9%84%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-r1625/" rel="">البدء مع إطار العمل جانغو لإنشاء تطبيق ويب</a>.
	</li>
</ul>
]]></description><guid isPermaLink="false">1810</guid><pubDate>Sun, 04 Dec 2022 18:00:00 +0000</pubDate></item><item><title>&#x623;&#x633;&#x627;&#x633;&#x64A;&#x627;&#x62A; &#x644;&#x63A;&#x629; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1689/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/63076e13c99e4_--.png.d4e376e5dbb85311bdc496395a4b105e.png" /></p>
<p>
	تُعَدّ <a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a> لغة برمجة تزيد من القدرة التفاعلية لمواقع ويب، وتشاهد ذلك مثلًا في الألعاب وفي مظاهر استجابة الصفحات عند نقر الأزرار أو عند إدخال البيانات إلى النماذج والاستمارات الإلكترونية، أو من خلال التغيير الديناميكي لتنسيق الصفحة أو عبر الرسوم المتحركة وغيرها، إذ سيساعدك هذا المقال لتبدأ استخدام جافاسكربت ويعرّفك أكثر بإمكانيات هذه اللغة.
</p>

<h2>
	تعرف على جافاسكربت
</h2>

<p>
	تُعَدّ <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-javascript-r664//" rel="">جافاسكربت</a> لغة برمجة قوية تزيد من القدرة التفاعلية لمواقع ويب، وقد ابتكرها برنارد آيتش Brendan Eich، وهو مؤسس مشارك لمشروع موزيللا ومؤسسة موزيللا وشركة موزيللا، كما تُعَدّ جافاسكربت لغةً قريبةً من المبرمجين المبتدئين، إذ ستتمكن مع الممارسة والخبرة من إنشاء ألعاب ثنائية وثلاثية الألعاب وبناء تطبيقات عصرية مرتبطة بقواعد بيانات وغير ذلك الكثير، وصحيح أنّ هذه اللغة مدمجة مع المتصفحات، لكنها مرنة، فقد بنى المطورون الكثير من الأدوات انطلاقًا من جافاسكربت، مقدِّمين كمًا واسعًا من القدرات الوظيفية بأقل مجهود ممكن، ونذكر منها :
</p>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">واجهات برمجية للتطبيقات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> مدمجة بالمتصفحات لتزويدها بوظائف إضافية مثل الإنشاء التلقائي لشيفرة HTML وضبط <a href="https://academy.hsoub.com/programming/css/%D8%A7%D9%84%D8%AA%D9%86%D8%B3%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-%D9%84%D9%84%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D9%81%D9%8A-css-r1054/" rel="">تنسيقات CSS</a> أو التقاط وتعديل الفيديوهات المصورة عن طريق كاميرا ويب أو توليد رسوميات ثلاثية الأبعاد ومقاطع صوتية.
	</li>
	<li>
		واجهات برمجية لتطبيقات صممها طرف ثالث تسمح للمطورين دمج وظائف يقدمها مزوّدو محتوى مثل تويتر وفيس بوك ضمن مواقع أخرى.
	</li>
	<li>
		أطر عمل ومكتبات صممها طرف ثالث ويمكن تطبيقها على صفحات HTML لتسريع بناء المواقع والتطبيقات.
	</li>
</ul>

<p>
	لا يغطي مجال هذا المقال تفاصيل الاختلاف بين الأدوات التي بُنيت وتبنى باستخدام جافاسكربت واللغة نفسها، لذلك يمكن دائمًا البحث عن تفاصيل مثل هذه عبر الإنترنت، وسيقدِّم لك القسم التالي من المقال بعض مزايا جافاسكربت البنيوية، كما سيمنحك إمكانية تجريب بعض مزايا الواجهات البرمجية الخاصة بالمتصفحات أيضًا.
</p>

<h2>
	كتابة أول برنامج في جافاسكربت
</h2>

<p>
	تُعَدّ جافاسكربت من أكثر التقنيات الحديثة شعبيةً، وستُدخِل مواقعك مع تقدم مهارتك فيها أبعادًا جديدةً من القوة والإبداع، لكن التآلف مع هذه اللغة أصعب من الاعتياد على العمل مع <a href="https://academy.hsoub.com/programming/html/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-html-r1687/" rel="">HTML</a> و <a href="https://academy.hsoub.com/programming/css/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-css-r1688/" rel="">CSS</a>، فقد تبدأ بالقليل ثم تنمو قدراتك تدريجيًا، ولنبدأ بتفحّص الطريقة التي سنضيف فيها جافاسكربت إلى صفحتك لتنفيذ برنامج "!Hello world"، وهو برنامج معياري لتقديم لغة برمجة إلى المستخدِم لأوّل مرة.
</p>

<p>
	<strong>تنبيه</strong>: إذا لم تكن لسبب ما متابعًا للأفكار التي تحدثنا عنها في مقالات سابقة، فيمكنك <a href="https://github.com/mdn/beginner-html-site-styled/archive/gh-pages.zip" rel="external nofollow">تنزيل</a> الشيفرة التجريبية للمثال واعتماده على أساس نقطة انطلاق.
</p>

<ol>
	<li>
		انتقل إلى المجلد الذي يضم موقعك التجريبي وانشئ ضمنه مجلدًا باسم scripts، ثم انشئ ضمن الأخير ملفًا نصيًا ثم احفظه بالاسم main.js.
	</li>
	<li>
		افتح الملف index.html واكتب قبل نهاية <a href="https://wiki.hsoub.com/HTML/body" rel="external">العنصر &lt;body&gt;</a> سطر الشيفرة التالية:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_14" style=""><span class="pun">&lt;</span><span class="pln">script src</span><span class="pun">=</span><span class="str">"scripts/main.js"</span><span class="pun">&gt;&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<ol start="3">
	<li>
		ينفِّذ هذا العنصر ما ينفذه تمامًا العنصر <code>&lt;link&gt;</code> عندما أدرج ملف تنسيق CSS، إذ يطبِّق هذا العنصر <a href="https://academy.hsoub.com/programming/javascript/%D9%86%D9%85%D8%B7-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r785/" rel="">شيفرة جافاسكربت</a> الموجودة في الملف على عناصر HTML في الصفحة بالإضافة إلى تنسيقات CCS أو أيّ شيء آخر.
	</li>
	<li>
		أضف الشيفرة التالية إلى الملف main.js:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_1788_18" style=""><span class="kwd">const</span><span class="pln"> myHeading </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'h1'</span><span class="pun">);</span><span class="pln">
myHeading</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Hello world!'</span><span class="pun">;</span></pre>

<ol start="5">
	<li>
		تأكد من حفظ التغيرات على الملفَين index.html وmain.js، ثم حمّل الملف index.html مجددًا في المتصفح الذي سيعرض صفحةً شبيهةً بالتالي:
	</li>
</ol>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="106377" href="https://academy.hsoub.com/uploads/monthly_2022_08/01_hello_world_prog.png.25cc19c3fb8d94aebe6ce21b9633be2d.png" rel=""><img alt="01_hello_world_prog.png" class="ipsImage ipsImage_thumbnailed" data-fileid="106377" data-unique="ft3jd3mn9" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_08/01_hello_world_prog.png.25cc19c3fb8d94aebe6ce21b9633be2d.png"></a>
</p>

<p>
	<strong>ملاحظة</strong>: يعود سبب وضع العنصر <a href="https://wiki.hsoub.com/HTML/script" rel="external"><code>&lt;script&gt;</code></a> في نهاية ملف HTML إلى طريقة القراءة المتسلسلة للشيفرة وفق ترتيب ظهور العناصر التي يتّبعها المتصفح.
</p>

<p>
	إذا حمّل المتصفح ملف جافاسكربت أولًا قبل عناصر HTML التي يُفترض أن يؤثر فيها، فقد تقع بعض المشاكل، لذلك من الأفضل تحميله بعد تحميل العناصر بوضع العنصر في نهاية صفحة HTML، فهذا سيحل المشكلة.
</p>

<h2>
	ما الذي حدث؟
</h2>

<p>
	لقد تغيّر عنوان الصفحة إلى "!Hello world" باستخدام جافاسكربت، إذ نُفِّذت العملية عبر استدعاء الدالة <code>()querySelector</code> التي التقطت مرجعًا إلى عنصر العنوان <code>&lt;h1&gt;</code> وخزّنته في المتغير <code>myHeading</code>، أي الأمر مشابه لما فعلناه باستخدام محددِّات CSS، فإذا أردت تطبيق شيء ما على عنصر، فلا بد من تحديده أولًا.
</p>

<p>
	بعد ذلك أُسندت إلى الخاصية <code>textContent</code> العائدة للمتغير <code>myHeading</code> والتي تمثل محتوى العنوان القيمة الجديدة "!Hello world".
</p>

<p>
	<strong>ملاحظة</strong>: تُعَدّ كلتا الميزتين المستخدَمين في هذا المثال جزءًا من الواجهة البرمجية <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">لشجرة DOM</a> التي تمتلك القدرة على التعامل مع مستند HTML.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة JavaScript
		</p>

		<p class="banner-subtitle">
			تعلم البرمجة بلغة جافا سكريبت انطلاقًا من أبسط المفاهيم وحتى بناء تطبيقات حقيقية.
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/javascript-application-development/" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة JavaScript" src="https://academy.hsoub.com/learn/assets/images/courses/javascript-application-development.png">
	</div>
</div>

<h2>
	أساسيات لغة جافاسكربت
</h2>

<p>
	سنشرح لك فيما يأتي بعض الميزات البنيوية لجافاسكربت لتفهم طريقة عملها بصورة أفضل، ومن الجدير بالذكر أنّ هذه الميزات مشتركة بين كل لغات البرمجة، فإذا أتقنت هذه الأساسيات، فستكون قد وضعت حجر الأساس لكتابة شيفرات بلغات أخرى.
</p>

<p>
	<strong>تنبيه</strong>: حاول أن تُدخل أسطر الشيفرة التجريبية التي تتعلمها في هذا المقال ضمن طرفية جافاسكربت JavaScript console المضمنة داخل المتصفح، ولمزيد من المعلومات عن هذه الطرفية راجع مقال <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D8%A7-%D9%87%D9%8A-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%85%D8%B7%D9%88%D8%B1%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D9%85%D8%AF%D9%85%D8%AC%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA%D8%9F-r1439/" rel="">أدوات مطوري ويب المدمجة في المتصفحات</a>.
</p>

<h3>
	المتغيرات
</h3>

<p>
	تُعدّ <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%91%D9%85%D8%A9-%D8%B9%D9%86-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D9%91%D8%B1%D8%A7%D8%AA-variables-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r612/" rel="">المتغيرات</a> حاويات لتخزين القيم، إذ تبدأ القصة عندما تُصرِّح عن متحول باستخدام التعليمة <code>var</code> (مع أنها غير محبّذة، ستجد التفاصيل لاحقًا) أو التعليمة <code>let</code> يتبعها الاسم الذي تختاره للمتغير:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_7" style=""><span class="pln">let myVariable</span><span class="pun">;</span></pre>

<p>
	تشير الفاصلة المنقوطة في نهاية السطر إلى نهاية الجملة البرمجية، وتحتاجها فقط عندما تريد الفصل بين العبارات البرمجية في السطر ذاته، لكن وضع فاصلة منقوطة في نهاية كل سطر هي عادة برمجية جيدة، وهنالك عدة قواعد أخرى عن وجوب استخدام الفاصلة المنقوطة وعدم وجوب ذلك، كما يمكنك تسمية المتغيِّر أيّ اسم تقريبًا مع بعض القيود، إذ يمكن الاطلاع على <a href="https://wiki.hsoub.com/JavaScript#.D8.AA.D8.B9.D8.A7.D8.A8.D9.8A.D8.B1_.D8.A7.D9.84.D8.AA.D8.B5.D8.B1.D9.8A.D8.AD_.D8.B9.D9.86_.D9.85.D8.AA.D8.BA.D9.8A.D8.B1.D8.A7.D8.AA" rel="external">توثيق المتغيرات</a> في موسوعة حسوب، وإذا لم تكن متأكدًا مما كتبت، فتستطيع استخدام بعض الخدمات على الإنترنت <a href="https://mothereff.in/js-variables" rel="external nofollow">للتحقق من صحة تسمية المتغيرات</a>، كما ينبغي الانتباه إلى أنّ <a href="http://%20https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-javascript-r664/" rel="external nofollow">جافاسكربت</a> حساسة لحالة الأحرف، فالمتغير <code>myVariable</code> يختلف تمامًا عن <code>myvariable</code>، لذلك تحقق من مشاكل مثل هذه عندما تواجهك الأخطاء.
</p>

<p>
	يمكنك إسناد قيمة إلى المتغير بعد التصريح عنه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_23" style=""><span class="pln">myVariable </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Bob'</span><span class="pun">;</span></pre>

<p>
	يمكنك تنفيذ خطوتَي التصريح والإسناد في سطر واحد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_25" style=""><span class="pln">let myVariable </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Bob'</span><span class="pun">;</span></pre>

<p>
	استدع المتغير وحسب لتحصل على القيمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_27" style=""><span class="pln">myVariable</span><span class="pun">;</span></pre>

<p>
	يمكن تغيير قيمة المتغير لاحقًا في مواضع أخرى في الشيفرة:
</p>

<pre class="ipsCode">let myVariable = 'Bob';
myVariable = 'Steve';
</pre>

<p>
	يمكن أن تحمل المتغيرات قيمًا من <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r672/" rel="">أنواع مختلفة</a>:
</p>

<ol>
	<li>
		<strong>String</strong>: يمثل مجموعةً من محارف تمثل سلسلةً نصيةً، ولابد من وضع السلسلة بين إشارتَي إقتباس مفردتين كما يلي:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_29" style=""><span class="pln">let myVariable </span><span class="pun">=</span><span class="str">'Bob'</span><span class="pun">;</span></pre>

<ol start="2">
	<li>
		<strong>Number</strong>: يمثل عددًا، ولا يُوضَع ضمن شارتي تنصيص.
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_32" style=""><span class="pln">let myVariable </span><span class="pun">=</span><span class="lit">10</span><span class="pun">;</span></pre>

<ol start="3">
	<li>
		<strong>Boolean</strong>: ويمثل أحد القيمين المنطقيتين: صحيح <code>true</code> أو خاطئ <code>false</code>، وهاتين الكلمتين من الكلمات الخاصة المحجوزة في لغة جافاسكربت ولا حاجة لوضعهما بين إشارتي تنصيص:
	</li>
</ol>

<pre class="ipsCode">let myVariable  = true;
</pre>

<ol start="4">
	<li>
		<strong>Array</strong>: يمثل مايُدعى بالمصفوفة، وهي بنية لتخزين عدة قيم تحت مرجع واحد:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_34" style=""><span class="pln">let myVariable </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="str">'bob'</span><span class="pun">,</span><span class="lit">10</span><span class="pun">,</span><span class="str">'mad'</span><span class="pun">]</span><span class="pln">
</span><span class="com">// يمكن الإشارة إلى كل عنصر من عناصر المصفوفة كما يلي</span><span class="pln">
myVariable</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln"> </span><span class="com">// 1 تعرض أولى قيم المصفوفة وهي </span><span class="pln">
myVariable</span><span class="pun">[</span><span class="lit">3</span><span class="pun">];</span><span class="pln"> </span><span class="com">// 'mad' تعرض القيمة الأخيرة </span></pre>

<ol start="5">
	<li>
		<strong>Object</strong>: قد يحمل أي نوع من القيم، فكل شيء في جافاسكربت هو كائن Object يمكن أن يُخزَّن في متغير:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_36" style=""><span class="pln">let myVariable </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'h1'</span><span class="pun">);</span><span class="pln">
</span><span class="com">// الشيفرة نفسها التي استخدمناها سابقًا</span></pre>

<p>
	لِمَ نحتاج المتغيِّرات إذًا؟ لأنها ضروية لتنفيذ كل ما له معنى في البرمجة، فالمتغيرات مخازن القيم، ولن تحصل على أية أفعال ديناميكية مثل تخصيص رسالة ترحيب أو تغيير الصورة المعروضة إذا لم تتغير القيم.
</p>

<h3>
	التعليقات
</h3>

<p>
	تُعَدّ التعليقات Comments مقتطفات نصيةً تُضاف إلى الشيفرة ويتجاهلها المتصفح، كما يمكن استخدام التعليقات في جافاسكربت الأسباب نفسها التي تستخدمها في CSS، ويشار إلى نص على أنه تعليق كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_38" style=""><span class="com">/*
Everything in between is a comment.
*/</span></pre>

<p>
	لكي يمتد التعليق على عدة أسطر، أو كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_40" style=""><span class="com">// This is a comment</span></pre>

<p>
	إذا كان التعليق على سطر واحد.
</p>

<h3>
	العوامل
</h3>

<p>
	يُعَدّ العامل Operator رمزًا رياضيًا يعطي نتيجةً استنادًا إلى قيمتين أو متغيرين، وإليك بعض العوامل الأبسط التي تُستخدَم في جافاسكربت مع بعض الأمثلة، وحاول تجريبها على طرفية جافاسكربت:
</p>

<ul>
	<li>
		الجمع: رمزه <code>+</code>، ويضيف عددين معًا أو يضم نصين إلى بعضهما:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_44" style=""><span class="lit">9</span><span class="pun">+</span><span class="lit">6</span><span class="pun">;</span><span class="pln"> </span><span class="com">//15</span><span class="pln">
</span><span class="str">'hello'</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'world'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// helloworld</span></pre>

<ul>
	<li>
		الطرح والضرب والقسمة: رموزها بالترتيب <code>-</code>، <code>*</code>، <code>/</code>، وعملها مشابه تمامًا لعملها الرياضي:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_42" style=""><span class="lit">4</span><span class="pun">-</span><span class="lit">5</span><span class="pun">;</span><span class="pln"> </span><span class="com">//-1</span><span class="pln">
</span><span class="lit">3</span><span class="pun">*</span><span class="lit">3</span><span class="pun">;</span><span class="com">//9</span><span class="pln">
</span><span class="lit">10</span><span class="pun">/</span><span class="lit">2</span><span class="pun">;</span><span class="com">//5</span></pre>

<ul>
	<li>
		الإسناد: رمزه <code>=</code>، ويسند قيمة إلى متغير:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_46" style=""><span class="pln">let myVar </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span></pre>

<ul>
	<li>
		المساواة: رمزها <code>===</code>، وتختبر تساوي قيمتين وتعيد نتيجةً منطقيةً؛ إما صحيح <code>true</code> أو خاطئ <code>false</code> :
	</li>
</ul>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_48" style=""><span class="pln">let myVar </span><span class="pun">=</span><span class="lit">3</span><span class="pun">;</span><span class="pln">
myVar </span><span class="pun">===</span><span class="lit">4</span><span class="pun">;</span><span class="pln"> </span><span class="com">//fals تعيد </span></pre>

<ul>
	<li>
		النفي: رمزه <code>!</code>، ويعيد القيمة المنطقية المعاكسة لما يتقدمها، إذ تُغيِّر <code>true</code> إلى <code>false</code> وبالعكس:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_50" style=""><span class="pln">let myVar</span><span class="pun">=</span><span class="lit">3</span><span class="pun">;</span><span class="pln">
</span><span class="pun">!(</span><span class="pln">myVar</span><span class="pun">===</span><span class="lit">4</span><span class="pun">);</span><span class="pln"> </span><span class="com">//true تعيد</span></pre>

<ul>
	<li>
		عدم المساواة: رمزها <code>‎!==‎</code>، وتختبر عدم تساوي قيمتين وتعيد النتيجة المنطقية المناسبة:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_52" style=""><span class="pln">let myVar</span><span class="pun">=</span><span class="lit">4</span><span class="pun">;</span><span class="pln">
myVar</span><span class="pun">==!</span><span class="lit">3</span><span class="pun">;</span><span class="pln"> </span><span class="com">//true تعيد</span></pre>

<p>
	هناك الكثير من <a href="https://wiki.hsoub.com/%D8%AA%D8%B5%D9%86%D9%8A%D9%81:JavaScript_Operator" rel="external">العوامل الأخرى</a> في جافاسكربت، لكننا سنكتفي الآن بما ذكرناه.
</p>

<p>
	<strong>ملاحظة</strong>: قد يقود دمج أنواع مختلفة من البيانات عند إجراء العمليات الحسابية إلى أخطاء، لذا فكن حذِرًا بالإشارة إلى متغيراتك لتحصل على النتيجة المتوقعة، فإذا نفَّذت العملية <code>'35' +'25'</code>، فستحصل على <code>2535</code> وهذا ما قد لا تتوقعه، لأن إشارات التنصيص المفردة تجعل ما داخلها نصوصًا لا أعدادًا، بينما إذا نفَّذت العملية على الصورة <code>35 + 25</code>، فستحصل على نتيجة الجمع الصحيحة.
</p>

<h3>
	العبارات الشرطية
</h3>

<p>
	تُعَدّ العبارات الشرطية بُنًى تُستخدَم لاختبار تحقق شرط معيَن نتيجته <code>true</code> أو <code>false</code>، ومن أكثر الصيغ الشرطية استخدامًا هي العبارة <code>if...else</code>، وإليك مثالًا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_54" style=""><span class="pln">let iceCream </span><span class="pun">=</span><span class="pln"> </span><span class="str">'chocolate'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">(</span><span class="pln">iceCream </span><span class="pun">===</span><span class="pln"> </span><span class="str">'chocolate'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">'Yay, I love chocolate ice cream!'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">'Awwww, but chocolate is my favorite...'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تختبر البنية الشرطية العبارة التي تقع ضمن <code>(...)if</code>، إذ تستخدِم البنية في الشيفرة السابقة عامل المساواة للموازنة بين قيمتي المتغّير <code>iceCream</code> والنص <code>chocolate</code> والتحقق من تساويهما، فإذا أعادت الموازنة القيمة <code>true</code>، فستُنفَّذ الكتلة الأولى من الشيفرة التي تلي تعليمة الشرط <code>(...)if</code>، وإلا فستُنفَّذ الكتلة الثانية التي تقع بعد العبارة <code>else</code>.
</p>

<h3>
	الدوال
</h3>

<p>
	تُعَدّ الدوال طريقةً لتنظيم الشيفرة التي ترغب في استخدامها مرارًا، إذ يمكنك أن تُعرِّف مثلًا مجموعةً من أسطر الشيفرة على أساس دالة يجري تنفيذها عندما تستدعيها باسمها في أي مكان من الشيفرة، إذ تملك هذه الطريقة فعاليةً كبيرةً في منع تكرار كتابة الشيفرة نفسها كلما احتجنا لها، ولقد رأينا في الشيفرات السابقة نماذجًا لدوال مثل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_58" style=""><span class="pln">let myVariable </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'h1'</span><span class="pun">);</span><span class="com">//هي دالة querySelector</span></pre>

<p>
	وكذلك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_60" style=""><span class="pln">alert</span><span class="pun">(</span><span class="str">'hello!'</span><span class="pun">);</span></pre>

<p>
	إنّ الدالتَين <code>document.querySelector</code> و <code>alert</code> مدمجتان مع المتصفح، فإذا رأيت شيئًا يشبه المتغير متبوعًا بقوسين <code>()</code>، فهو دالة على الأغلب، كما تأخذ الدوال أشياء تُدعى وسائط arguments، وهي بيانات تحتاجها لتنفيذ وظائفها، إذ تُوضع الوسائط داخل قوسَي الدالة وتفصل بينها فاصلة <code>,</code>.
</p>

<p>
	تعرض الدالة <code>alert</code> على سبيل المثال نافذةً منبثقةً ضمن المتصفح، فلا بد من تزويدها بنص على هيئة وسيط لكي تعرضه، كما يمكنك أيضًا تعريف دالة خاصة بك، إذ سننشئ في المثال التالي دالةً تأخذ وسيطَين عددين ثم تعيد ناتج جدائهما:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_62" style=""><span class="kwd">function</span><span class="pln"> multiply</span><span class="pun">(</span><span class="pln">num1</span><span class="pun">,</span><span class="pln">num2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let result </span><span class="pun">=</span><span class="pln"> num1 </span><span class="pun">*</span><span class="pln"> num2</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	حاول تنفيذ ذلك في الطرفية، ثم اختبر الدالة بتغيير قيم الوسيطَين كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_64" style=""><span class="pln">multiply</span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">);</span><span class="pln">
multiply</span><span class="pun">(</span><span class="lit">20</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span><span class="pln">
multiply</span><span class="pun">(</span><span class="lit">0.5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">);</span></pre>

<p>
	<strong>ملاحظة</strong>: تخبر التعليمة <code>return</code> في نهاية الدالة أن تعيد قيمةً هي نتيجة تنفيذ الدالة غالبًا -مثل <code>result</code> في المثال السابق- لكي تستطيع استخدامها، وهذا الأمر ضروري لأن المتغيرات التي تُعرَّف داخل الدالة لا يمكن استخدامها خارج الدالة وهذا ما يُعرَف بمجال رؤية المتغير variable scoop.
</p>

<h3>
	الأحداث
</h3>

<p>
	لابد من معالجة الأحداث التي تقع في الصفحة لتظهر تفاعلية الصفحة، فالأحداث events هي بنى برمجية تُنصِت إلى النشاطات التي تجري في المتصفح وتستجيب لها بتنفيذ شيفرة محددة، ومن أكثر الأحداث وضوحًا هو حدث النقر على الفأرة والذي يقع عندما تنقر بالفأرة على شيء ما ضمن الصفحة، ولشرح الفكرة، أدخِل الشيفرة التالية في طرفية جافاسكربت ثم انقر على الصفحة التي يعرضها المتصفح:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_66" style=""><span class="pln">document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'html'</span><span class="pun">).</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'click'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">'Ouch! Stop poking me!'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	هناك طرق عدة لربط معالِج الحدث event handler بالعنصر، فقد اخترنا في الشيفرة السابقة العنصر <code>&lt;html&gt;</code>، ومن ثم استدعينا معه الدالة <code>addEventListener()‎</code> مع تحديد اسم الحدث (في حالتنا 'click' النقر) المراد الإنصات له مع تحديد الدالة المراد تنفيذها عند وقوع ذلك الحدث.
</p>

<p>
	لاحظ الشيفرة التالية التي تشبه تمامًا الشيفرة السابقة في العمل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_68" style=""><span class="pln">document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'html'</span><span class="pun">).</span><span class="pln">onclick </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">'Ouch! Stop poking me!'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أسندنا فيها الخاصية <code>onclick</code> التي تمثل معالج حدث النقر (الذي يكون اسم الحدث مسبوقًا بكلمة on) إلى دالة دون اسم anonymous تضم الشيفرة التي نريد تنفيذها عندما يقع حدث النقر.
</p>

<p>
	أيضًا الشيفرة السابقة والتي تسبقها مماثل للشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_70" style=""><span class="pln">let myHTML </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'html'</span><span class="pun">);</span><span class="pln">
myHTML</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'click'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">'Ouch! Stop poking me!'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	لكنه أقصر.
</p>

<p>
	الدالة السابقة التي ممرناها إلى الدالة <code>addEventListener()‎</code> تدعى دالة مجهولة لعدم امتلاكها اسمًا، وهنالك طريقة أخرى لكتابة دوال مجهولة تدعى <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%B3%D9%87%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r782/" rel="">الدوال السهمية</a> التي تستعمل الصيغة <code>‎() =&gt;‎</code> بدلًا من <code>function ()‎</code>، انظر مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_72" style=""><span class="pln">document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'html'</span><span class="pun">).</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'click'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">'Ouch! Stop poking me!'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<h2>
	تطوير المثال التجريبي الذي نعمل عليه
</h2>

<p>
	لنضِف بعض الميزات الجديدة إلى صفحة الويب التي نطوّرها باستخدام ما تعلمناه عن جافاسكربت.
</p>

<p>
	احذف بداية محتوى الملف main.js واحفظ الملف فارغًا كي لا تتعارض الشيفرات الجديدة مع تلك التي كتبناها سابقًا.
</p>

<h3>
	تغيير للصورة
</h3>

<p>
	سنتعلم استخدام جافاسكربت وميزات واجهة <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">DOM</a> البرمجية لتبديل الصورة مرةً أو مرتين عند النقر عليها.
</p>

<ol>
	<li>
		اختر صورةً جديدةً لاستخدامها والأفضل أن يكون لها قياس الصورة الأولى نفسه.
	</li>
	<li>
		احفظ الصورة في المجلد images باسم firefox2.png.
	</li>
	<li>
		أضف شيفرة جافاسكربت التالية إلى الملف main.js:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_75" style=""><span class="pln">let myImage </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'img'</span><span class="pun">);</span><span class="pln">

myImage</span><span class="pun">.</span><span class="pln">onclick </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let mySrc </span><span class="pun">=</span><span class="pln"> myImage</span><span class="pun">.</span><span class="pln">getAttribute</span><span class="pun">(</span><span class="str">'src'</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">mySrc </span><span class="pun">===</span><span class="pln"> </span><span class="str">'images/firefox-icon.png'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      myImage</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">'src'</span><span class="pun">,</span><span class="str">'images/firefox2.png'</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      myImage</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">'src'</span><span class="pun">,</span><span class="str">'images/firefox-icon.png'</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<ol start="4">
	<li>
		احفظ جميع التغيرات وحمّل الملف index.html في المتصفح وانقر على الصورة، إذ يجب أن تتغير الصورة المعروضة عند النقر.
	</li>
</ol>

<p>
	<strong>إليك ما حدث</strong>: لقد خزنت مرجعًا إلى <a href="https://wiki.hsoub.com/HTML/img" rel="external">العنصر &lt;img&gt;</a> في المتغير <code>myImage</code>، ثم أسندت قيمة الخاصية <code>onclick</code> للمتغير والتي تمثل معالِج حدث النقر إلى دالة دون اسم لكي يُنفِّذ محتواها كلما نقرت على الصورة، وما تفعله الشيفرة هو استخلاص قيمة السمة <code>src</code> لعنصر الصورة واستخدم بنية شرطية للتحقق أن قيمتها تساوي مسار الصورة الأصلية، فإذا كانت كذلك، فستُغيِّر الشيفرة قيمة السمة <code>src</code> للعنصر <code>&lt;img&gt;</code> إلى مسار الصورة الثانية لكي يعرضها المتصفح، وإذا لم تكن كذلك، فهذا يعني أنّ قيمة السمة <code>src</code> قد تغيرت سابقًا وستعيدها الشيفرة إلى قيمتها الأصلية لتُعرض الصورة الأساسية وهكذا.
</p>

<h3>
	إضافة رسالة ترحيب خاصة
</h3>

<p>
	لنغيّر الآن عنوان الصفحة كي يعرض رسالة ترحيب خاصة بالمستخدِم عند زيارته للصورة وستبقى هذه الرسالة، ولإن غادر الزائر الصفحة وعاد مجددًا ستظهر الرسالة، لأننا سنخزنها باستخدام الواجهة البرمجية <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%85%D8%AD%D9%84%D9%8A%D8%A7-%D9%81%D9%8A-%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1338/" rel="">للتخزين على المتصفح</a>، وسنقدم أيضًا خيارًا لتغيير المستخدِم، وبالتالي تغيير رسالة الترحيب.
</p>

<ol>
	<li>
		أضف السطر التالي في الملف index.html فقط قبل العنصر <code>&lt;script&gt;</code>:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_86" style=""><span class="pun">&lt;</span><span class="pln">button</span><span class="pun">&gt;</span><span class="typ">Change</span><span class="pln"> user</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span></pre>

<p>
	أي أسفل الملف.
</p>

<ol start="2">
	<li>
		ضع الشيفرة التالية في نهاية الملف main.js كما هي تمامًا، إذ تُخزِّن هذه الشيفرة مرجعًا إلى الزر الجديد ومرجعًا إلى العنوان داخل متغيرَين:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_88" style=""><span class="pln">let myButton </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'button'</span><span class="pun">);</span><span class="pln">
let myHeading </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'h1'</span><span class="pun">);</span></pre>

<ol start="3">
	<li>
		أضف الدالة التالية لتخصيص رسالة الترحيب، إذ لن تفعل الدالة شيئًا بالطبع حتى اللحظة لكنها ستفعل قريبًا:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_90" style=""><span class="kwd">function</span><span class="pln"> setUserName</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let myName </span><span class="pun">=</span><span class="pln"> prompt</span><span class="pun">(</span><span class="str">'Please enter your name.'</span><span class="pun">);</span><span class="pln">
  localStorage</span><span class="pun">.</span><span class="pln">setItem</span><span class="pun">(</span><span class="str">'name'</span><span class="pun">,</span><span class="pln"> myName</span><span class="pun">);</span><span class="pln">
  myHeading</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Mozilla is cool, '</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> myName</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تحتوي الدالة <code>()setUserName</code> على الدالة <code>()prompt</code> التي تعرض مربع حوار على شاشة المتصفح بصورة مشابهة للدالة <code>()alert</code> لكنها تنتظر من المستخدِم إدخال قيمة لكي تخزنها بعد أن ينقر الزر موافق OK.
</p>

<p>
	نطلب في هذه الحالة من المستخدِم إدخال اسمه ثم تستدعي الشيفرة الواجهة البرمجية <code>localStorage</code> التي تسمح بتخزين البيانات في ذاكرة المتصفح للوصول إليها لاحقًا، كما نستخدِم الدالة <code>()setItem</code> لإنشاء وتخزين عنصر بيانات يُدعى <code>name</code> ثم إسناد قيمته إلى المتغير <code>myName</code> الذي يحوي القيمة التي يدخلها المستخدِم للاسم، ونضيف أخيرًا قيمة المتغير <code>myName</code> إلى محتوى العنوان ثم يُسند النص الناتح إلى الخاصية <code>textContent</code> ليظهر الاسم الذي أدخلناه على أساس جزء من العنوان.
</p>

<ol start="4">
	<li>
		أضف الكتلة الشرطية <code>if ... else</code> التالية:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_92" style=""><span class="kwd">if</span><span class="pun">(!</span><span class="pln">localStorage</span><span class="pun">.</span><span class="pln">getItem</span><span class="pun">(</span><span class="str">'name'</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  setUserName</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let storedName </span><span class="pun">=</span><span class="pln"> localStorage</span><span class="pun">.</span><span class="pln">getItem</span><span class="pun">(</span><span class="str">'name'</span><span class="pun">);</span><span class="pln">
  myHeading</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Mozilla is cool, '</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> storedName</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكننا استدعاء هذه الشيفرة عند بداية تحميل الصفحة كونها شيفرة تهيئة للمحتوى، إذ يستخدِم السطر الأول منها عامل النفي المنطقي <code>!</code> للتحقق من عدم وجود عنصر البيانات <code>name</code> ضمن مخازن الذاكرة <code>LocalStorage</code>، فإذا لم يجده، فسيستدعي الدالة <code>()setUserName</code> لإنشاءه؛ أما إذا كان موجودًا -أي أنّ المستخدِم أدخله في أثناء زيارته الأولى-، فسنعيد قيمته باستخدام الدالة <code>()getItem</code> ثم نضبط قيمة محتوى العنوان ليصبح النص الأصلي إضافة إلى اسم المستخدِم كما فعلنا ضمن الدالة <code>()setUserName</code>.
</p>

<ol start="5">
	<li>
		أضف معالِج الحدث <code>onclick</code> التالي إلى الزر لكي تُستدعى الدالة <code>()setUserName</code> عند النقر عليه، وبالتالي سيتمكن المستخدِم من تغيير الاسم عند النقر على هذا الزر:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_94" style=""><span class="pln">myButton</span><span class="pun">.</span><span class="pln">onclick </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  setUserName</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	ظهور القيمة null
</h3>

<p>
	إذا نقرت زر إلغاء cancel بدل زر موافق ok أثناء ظهور مربع الحوار، فسيظهر لك عنوان الصفحة "Mozilla is cool, null"، ويعود السبب في ذلك إلى عدم إسناد قيمة إلى المتغير <code>myName</code> وبالتالي سيأخذ القيمة <code>null</code>، وهي قيمة خاصة في جافاسكربت تشير إلى غياب قيمة مطلوبة؛ أما إذا نقرت زر موافق ok دون إدخال اسم، فستكون النتيجة ",Mozilla is cool" وهذا أمر واضح، ولتفادي هذه المشاكل يمكنك التحقق من وجود الاسم فعلًا، لذلك سنعدِّل شيفرة الدالة <code>()setUserName</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1788_96" style=""><span class="kwd">function</span><span class="pln"> setUserName</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let myName </span><span class="pun">=</span><span class="pln"> prompt</span><span class="pun">(</span><span class="str">'Please enter your name.'</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">if</span><span class="pun">(!</span><span class="pln">myName</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setUserName</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    localStorage</span><span class="pun">.</span><span class="pln">setItem</span><span class="pun">(</span><span class="str">'name'</span><span class="pun">,</span><span class="pln"> myName</span><span class="pun">);</span><span class="pln">
    myHeading</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Mozilla is cool, '</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> myName</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يعني هذا إعادة استدعاء الدالة <code>()setUserName</code> من جديد إذا لم يكن للمتغير قيمة؛ أما إذا كان له قيمة، فستُخزَّن ضمن <code>localStorage</code> وتُضاف إلى العنوان.
</p>

<h2>
	خلاصة
</h2>

<p>
	إذا اتبعت التوجيهات التي أشرنا إليها خلال اطلاعك على هذا المقال، فستبدو صفحة الويب التي نبنيها بالشكل التالي تقريبًا:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="106378" href="https://academy.hsoub.com/uploads/monthly_2022_08/02_your_page_so_far.png.6c35a945f92d06be9cc2a0191b014100.png" rel=""><img alt="02_your_page_so_far.png" class="ipsImage ipsImage_thumbnailed" data-fileid="106378" data-unique="2ivcdmw9c" src="https://academy.hsoub.com/uploads/monthly_2022_08/02_your_page_so_far.thumb.png.d47bd5bdfce06af32dceeeb4605ee228.png"></a>
</p>

<p>
	إذا لم تجد عملك صحيحًا، فيمكنك دائمًا موازنة ما فعلته مع <a href="https://github.com/mdn/beginner-html-site/blob/gh-pages/index.html" rel="external nofollow">النسخة الجاهزة</a> على جيت-هاب.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics" rel="external nofollow">JavaScript basics</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%B5%D9%81%D8%B1-%D8%AD%D8%AA%D9%89-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D8%B1%D8%A7%D9%81-r2046/" rel="">تعلم لغة جافا سكريبت من الصفر حتى الاحتراف</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/JavaScript" rel="external">توثيق لغة JavaScript العربي</a> 
	</li>
	<li>
		<a href="https://academy.hsoub.com/learn-programming/" rel="">تعلم البرمجة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%A7-%D9%87%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%9F-r524/" rel="">ما هي جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">الدليل السريع إلى لغة البرمجة جافاسكريبت JavaScript</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/search/?&amp;page=4&amp;tags=%D8%AF%D9%84%D9%8A%D9%84%20%D8%AA%D8%B9%D9%84%D9%85%20%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA&amp;sortby=newest" rel="">سلسلة دليل تعلم جافاسكربت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1689</guid><pubDate>Sun, 25 Sep 2022 16:09:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x648;&#x62C;&#x64A;&#x647; Routing &#x641;&#x64A; &#x625;&#x637;&#x627;&#x631; &#x627;&#x644;&#x639;&#x645;&#x644; Ember</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%8A%D9%87-routing-%D9%81%D9%8A-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-ember-r1663/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/62f77ebdbfb13_----Ember---.png.7ef58c4d0c43e561b256772408377c3c.png" /></p>

<p>
	سنتعرف في هذا المقال على التوجيه Routing أو الترشيح المستند إلى <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87-r1435/" rel="">عنوان URL</a> كما يشار إليه في بعض الأحيان، والذي سنستخدمه لتوفير عنوان URL فريد لكل عرض من عروض المهام الثلاثة: "جميع المهام All" و"المهام النشطة Active" و"المهام المكتملة Completed"، كما سنتطرق إلى كيفية استكشاف الأخطاء وإصلاحها.
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a>، ومعرفة استخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">سطر الأوامر أو الطرفية</a>، إذ يُعَدّ فهم ميزات جافاسكربت الحديثة مثل <a href="https://wiki.hsoub.com/JavaScript/class" rel="external">الأصناف Classes</a> و<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1286/" rel="">الوحدات Modules</a> وما إلى ذلك مفيدًا للغاية، لأن إطار العمل Ember يستخدمها بكثرة.
	</li>
	<li>
		<strong>الهدف</strong>: التعرف على كيفية تطبيق التوجيه في إطار العمل Ember، وتوفير مزيد من الموارد لتعلم إطار عمل Ember ومعلومات استكشاف الأخطاء وإصلاحها.
	</li>
</ul>
<h2>
	الترشيح المستند إلى عنوان URL
</h2>

<p>
	يأتي إطار عمل Ember مع نظام التوجيه ذي التكامل الوثيق مع عنوان URL الخاص بالمتصفح، إذ يجب عند كتابة تطبيقات الويب تمثيل الصفحة بعنوان URL بحيث إذا كانت الصفحة بحاجة إلى تحديث، فلن يُفاجَأ المستخدِم بحالة تطبيق الويب، وسيتمكّن من الانتقال مباشرةً إلى عروض التطبيق المهمة.
</p>

<p>
	لدينا حاليًا صفحة "جميع المهام All" إذ لن نطبّق ترشيحًا فيها في الوقت الحالي، لكن يجب إعادة تنظيمها قليلًا للتعامل مع عرض مختلف للمهام النشطة والمهام المكتملة، كما يحتوي تطبيق Ember على مسار تطبيق افتراضي مرتبط بالقالب app/templates/application.hbs الذي يُعَدّ نقطة الدخول إلى تطبيق قائمة المهام، لذلك يجب إجراء بعض التغييرات للسماح بالتوجيه Routing.
</p>

<h2>
	إنشاء المسارات
</h2>

<p>
	لننشئ ثلاثة مسارات جديدة هي: "Index" و "Active" و "Completed" من خلال إدخال الأوامر التالية في الطرفية ضمن المجلد الجذر لتطبيقك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_11" style="">
<span class="pln">ember generate route index
ember generate route completed
ember generate route active</span></pre>

<p>
	لا ينتج عن الأمرَين الثاني والثالث ملفات جديدةً فحسب، وإنما يُعدَّل الملف app/router.js الموجود مسبقًا الذي يحتوي على المحتويات التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_13" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">EmberRouter</span><span class="pln"> from </span><span class="str">'@ember/routing/router'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> config from </span><span class="str">'./config/environment'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Router</span><span class="pln"> extends </span><span class="typ">EmberRouter</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  location </span><span class="pun">=</span><span class="pln"> config</span><span class="pun">.</span><span class="pln">locationType</span><span class="pun">;</span><span class="pln">
  rootURL </span><span class="pun">=</span><span class="pln"> config</span><span class="pun">.</span><span class="pln">rootURL</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="typ">Router</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">route</span><span class="pun">(</span><span class="str">'completed'</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">route</span><span class="pun">(</span><span class="str">'active'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يتصرّف الملف router.js بوصفه خريطة موقع sitemap للمطورين ليتمكنوا من رؤية كيفية تنظيم التطبيق بأكمله بسرعة، كما أنه يخبر <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-ember-r1648/" rel="">إطار العمل Ember</a> بكيفية التفاعل مع مسارك عند تحميل بيانات عشوائية أو التعامل مع الأخطاء أثناء تحميل تلك البيانات أو تفسير الأجزاء الآلية لعنوان URL مثلًا، وبما أنّ بياناتنا ساكنة، فلن نصل إلى أيّ من هذه الميزات الرائعة، لكننا سنظل نتأكد من أنّ المسار يوفِّر الحد الأدنى من البيانات المطلوبة لعرض الصفحة.
</p>

<p>
	لم يضِف إنشاء المسار "Index" سطر تعريف مسار إلى الملف router.js، لأن "Index" هي كلمة خاصة تشير إلى المسار الافتراضي للتصيير والتحميل وغير ذلك كما هو الحال مع التنقل باستخدام عنوان URL وتحميل وحدة <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">جافاسكربت JavaScript</a>.
</p>

<p>
	يمكنك تعديل طريقتنا القديمة في تصيير تطبيق قائمة المهام من خلال استبدال الاستدعاء <code>{{outlet}}</code> باستدعاء المكون TodoList من قالب التطبيق ب، مما يعني تصيير أيّ مسار فرعي في المكان نفسه دون الانتقال إلى تبويب جديد.
</p>

<p>
	انتقل إلى الملف todomvc/app/templates/application.hbs وضَع مكان السطر التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2362_23" style="">
<span class="tag">&lt;TodoList</span><span class="pln"> </span><span class="tag">/&gt;</span></pre>

<p>
	ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_21" style="">
<span class="pun">{{</span><span class="pln">outlet</span><span class="pun">}}</span></pre>

<p>
	يمكننا الآن إدخال استدعاء المكوّن TodoList في قوالب index.hbs و completed.hbs و active.hbs الموجودة أيضًا في مجلد القوالب.
</p>

<p>
	ضَع مكان السطر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_25" style="">
<span class="pun">{{</span><span class="pln">outlet</span><span class="pun">}}</span></pre>

<p>
	ما يلي:
</p>

<pre class="ipsCode">
&lt;TodoList /&gt;
</pre>

<p>
	إذا جرّبت التطبيق مرةً أخرى وزرتَ أيًّا من المسارات الثلاثة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_27" style="">
<span class="pln">localhost</span><span class="pun">:</span><span class="lit">4200</span><span class="pln"> 
localhost</span><span class="pun">:</span><span class="lit">4200</span><span class="pun">/</span><span class="pln">active 
localhost</span><span class="pun">:</span><span class="lit">4200</span><span class="pun">/</span><span class="pln">completed</span></pre>

<p>
	فسترى الشيء نفسه بالضبط، إذ سيصيَّر القالب الذي يتوافق مع المسار المحدد ("Active" أو "Completed" أو "Index") في كل عنوان URL للمكون <code>&lt;TodoList /‎&gt;</code>.
</p>

<p>
	يُحدَّد الموقع في الصفحة حيث يُصيَّر المكوّن <code>&lt;TodoList /‎&gt;</code> باستخدام <code>{{ outlet }}</code> في المسار الأب وهو في هذه الحالة application.hbs، ولدينا الآن مساراتنا في مكانها الصحيح، لكننا بحاجة إلى طريقة للتمييز بين كل من هذه المسارات لتظهِر ما يفترض منها أن تظهِره.
</p>

<p>
	ارجع مرةً أخرى إلى الملف todo-data.js الذي يحتوي مسبقًا على تابع جالب getter يعيد جميع المهام وتابع جالب آخر يعيد المهام غير المكتملة، ولكنه لا يحتوي على تابع جالب يعيد المهام المكتملة فقط، لذا لنضفه بعد التوابع الجالبة الموجودة مسبقًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_29" style="">
<span class="kwd">get</span><span class="pln"> completed</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">todo </span><span class="pun">=&gt;</span><span class="pln"> todo</span><span class="pun">.</span><span class="pln">isCompleted</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	النماذج
</h2>

<p>
	يجب الآن إضافة نماذج Models إلى مسارات ملفات جافاسكربت للسماح بسهولة بإعادة مجموعات بيانات معينة لعرضها في تلك النماذج، إذ يُعَدّ النموذج <code>model</code> بأنه خطاف Hook دورة حياة تحميل بيانات، ولكن ليست قدرات النموذج مهمةً لنا في تطبيق TodoMVC، كما سنوفر الوصول إلى الخدمة كما فعلنا مع المكوّنات.
</p>

<h3>
	نموذج المسار index
</h3>

<p>
	عدِّل أولًا الملف todomvc/app/routes/index.js ليبدو كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_31" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">Route</span><span class="pln"> from </span><span class="str">'@ember/routing/route'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> inject as service </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@ember/service'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">IndexRoute</span><span class="pln"> extends </span><span class="typ">Route</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">@service</span><span class="pun">(</span><span class="str">'todo-data'</span><span class="pun">)</span><span class="pln"> todos</span><span class="pun">;</span><span class="pln">

  model</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">get</span><span class="pln"> allTodos</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> todos</span><span class="pun">.</span><span class="pln">all</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكننا الآن تعديل الملف todomvc/app/templates/index.hbs بحيث إذا تضمّن المكوّن <code>&lt;TodoList /‎&gt;</code>، فإنه يفعل ذلك صراحة مع النموذج المتاح، ويستدعي التابع الجالب <code>allTodos()‎</code> للتأكد من ظهور جميع المهام.
</p>

<p>
	عدِّل السطر التالي في هذا الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_57" style="">
<span class="pun">&lt;</span><span class="typ">TodoList</span><span class="pln"> </span><span class="pun">/&gt;</span></pre>

<p>
	إلى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_59" style="">
<span class="pun">&lt;</span><span class="typ">TodoList</span><span class="pln"> </span><span class="lit">@todos</span><span class="pun">={{</span><span class="pln"> </span><span class="lit">@model</span><span class="pun">.</span><span class="pln">allTodos </span><span class="pun">}}/&gt;</span></pre>

<h3>
	نموذج المسار completed
</h3>

<p>
	عدِّل الملف todomvc/app/routes/completed.js ليبدو كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_43" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">Route</span><span class="pln"> from </span><span class="str">'@ember/routing/route'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> inject as service </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@ember/service'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CompletedRoute</span><span class="pln"> extends </span><span class="typ">Route</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">@service</span><span class="pun">(</span><span class="str">'todo-data'</span><span class="pun">)</span><span class="pln"> todos</span><span class="pun">;</span><span class="pln">

  model</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">get</span><span class="pln"> completedTodos</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> todos</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكننا الآن تعديل الملف todomvc/app/templates/completed.hbs بحيث إذا تضمّن المكوّن <code>‎&lt;TodoList /‎&gt;‎</code>، فإنه يفعل ذلك صراحةً مع النموذج المتاح، ويستدعي التابع الجالب <code>completedTodos()‎</code> للتأكد من ظهور المهام المكتملة فقط.
</p>

<p>
	عدِّل السطر التالي في هذا الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_55" style="">
<span class="pun">&lt;</span><span class="typ">TodoList</span><span class="pln"> </span><span class="pun">/&gt;</span></pre>

<p>
	إلى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_47" style="">
<span class="pun">&lt;</span><span class="typ">TodoList</span><span class="pln"> </span><span class="lit">@todos</span><span class="pun">={{</span><span class="pln"> </span><span class="lit">@model</span><span class="pun">.</span><span class="pln">completedTodos </span><span class="pun">}}/&gt;</span></pre>

<h3>
	نموذج المسار active
</h3>

<p>
	عدِّل الملف todomvc/app/routes/active.js ليبدو كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_49" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">Route</span><span class="pln"> from </span><span class="str">'@ember/routing/route'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> inject as service </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@ember/service'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ActiveRoute</span><span class="pln"> extends </span><span class="typ">Route</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">@service</span><span class="pun">(</span><span class="str">'todo-data'</span><span class="pun">)</span><span class="pln"> todos</span><span class="pun">;</span><span class="pln">

  model</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">get</span><span class="pln"> activeTodos</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> todos</span><span class="pun">.</span><span class="pln">incomplete</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكننا الآن تعديل الملف todomvc/app/templates/active.hbs بحيث إذا تضمّن المكوّن <code>&lt;TodoList /‎&gt;</code>، فإنه يفعل ذلك صراحةً مع النموذج المتاح، ويستدعي التابع الجالب <code>activeTodos()‎</code> للتأكد من ظهور المهام النشطة أو غير المكتملة فقط.
</p>

<p>
	عدِّل السطر التالي في هذا الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_51" style="">
<span class="pun">&lt;</span><span class="typ">TodoList</span><span class="pln"> </span><span class="pun">/&gt;</span></pre>

<p>
	إلى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_53" style="">
<span class="pun">&lt;</span><span class="typ">TodoList</span><span class="pln"> </span><span class="lit">@todos</span><span class="pun">={{</span><span class="pln"> </span><span class="lit">@model</span><span class="pun">.</span><span class="pln">activeTodos </span><span class="pun">}}/&gt;</span></pre>

<p>
	لاحظ أننا نعيد كائنًا له تابع جالب بدلًا من كائن ساكن أو قائمة مهام ساكنة مثل <code>this.todos.completed</code> في خطافات نماذج المسار، والسبب في ذلك هو أننا نريد أن يكون للقالب مرجع آلي لقائمة المهام، فإذا أعدنا القائمة مباشرةً، فلن يُعاد حساب البيانات أبدًا، مما يؤدي إلى ظهور تنقلات فاشلة وعدم ترشيح فعلي، ويمكن إعادة البحث عن المهام من خلال تحديد تابع جالب في كائن الإعادة من بيانات النموذج بحيث تُمثَّل التعديلات التي أجريناها على قائمة المهام في القائمة المُصيَّرة.
</p>

<h2>
	تشغيل روابط التذييل
</h2>

<p>
	أصبحت وظائف المسار الآن في مكانها الصحيح، لكن لا يمكننا الوصول إليها من تطبيقنا، لذا لنفعِّل روابط التذييل بحيث يؤدي النقر عليها إلى الانتقال إلى المسارات المطلوبة.
</p>

<p>
	ارجع إلى الملف todomvc/app/components/footer.hbs وابحث عمّا يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2362_61" style="">
<span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"#"</span><span class="tag">&gt;</span><span class="pln">All</span><span class="tag">&lt;/a&gt;</span><span class="pln">
</span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"#"</span><span class="tag">&gt;</span><span class="pln">Active</span><span class="tag">&lt;/a&gt;</span><span class="pln">
</span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"#"</span><span class="tag">&gt;</span><span class="pln">Completed</span><span class="tag">&lt;/a&gt;</span></pre>

<p>
	وعدِّله إلى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_63" style="">
<span class="pun">&lt;</span><span class="typ">LinkTo</span><span class="pln"> </span><span class="lit">@route</span><span class="pun">=</span><span class="str">'index'</span><span class="pun">&gt;</span><span class="typ">All</span><span class="pun">&lt;/</span><span class="typ">LinkTo</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="typ">LinkTo</span><span class="pln"> </span><span class="lit">@route</span><span class="pun">=</span><span class="str">'active'</span><span class="pun">&gt;</span><span class="typ">Active</span><span class="pun">&lt;/</span><span class="typ">LinkTo</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="typ">LinkTo</span><span class="pln"> </span><span class="lit">@route</span><span class="pun">=</span><span class="str">'completed'</span><span class="pun">&gt;</span><span class="typ">Completed</span><span class="pun">&lt;/</span><span class="typ">LinkTo</span><span class="pun">&gt;</span></pre>

<p>
	يُعَدّ <code>&lt;LinkTo&gt;</code> بأنه مكوّن Ember مبني مسبقًا يعالِج جميع تغييرات الحالة عند التنقل بين المسارات، ويضبط الصنف <code>active</code> على أيّ رابط يطابق عنوان URL في حالة وجود رغبة في تنسيقه تنسيقًا مختلفًا عن الروابط غير النشطة.
</p>

<h2>
	تحديث عرض المهام ضمن قائمة المهام
</h2>

<p>
	أحد الأشياء الصغيرة الأخيرة التي نحتاج إلى إصلاحها هو أننا كنا ندخل سابقًا إلى خدمة <code>todo-data</code> مباشرةً ونكرّر جميع المهام ضمن الملف todomvc/app/components/todo-list.hbs كما يلي:
</p>

<pre class="ipsCode">
{{#each this.todos.all as |todo| }}
</pre>

<p>
	بما أننا نريد أن يعرض مكوّن قائمة المهام قائمةً مُرشَّحةً، فيجب تمرير وسيط إلى مكوِّن قائمة المهام يمثِّل قائمة المهام الحالية من <code>todos</code> كما يلي:
</p>

<pre class="ipsCode">
{{#each @todos as |todo| }}
</pre>

<p>
	يجب أن يحتوي تطبيقك الآن على روابط فعّالة في التذييل تعرض مسارات "Index" أو المسارات الافتراضية و"النشطة Active" و"المكتملة Completed".
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="105333" href="https://academy.hsoub.com/uploads/monthly_2022_08/01_todos-navigation.gif.e4fe3caa77ecfa0fa462cec12bcc0f46.gif" rel=""><img alt="01_todos-navigation.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="105333" data-unique="1jjbub2o0" src="https://academy.hsoub.com/uploads/monthly_2022_08/01_todos-navigation.thumb.gif.ab85ced0d73cf780b3729e1595d6b97d.gif" style="width: 550px; height: auto;"></a>
</p>

<p>
	هناك الكثير مما يجب تنفيذه قبل أن يتطابق ما نفّذناه حتى الآن مع <a href="https://todomvc.com/" rel="external nofollow">تطبيق TodoMVC الأصلي</a> مثل تعديل المهام وحذفها واستمرارها عبر عمليات إعادة تحميل الصفحة، كما يمكنك مشاهدة تطبيق Ember المكتمل من خلال التحقق من مجلد التطبيق النهائي في <a href="https://github.com/NullVoxPopuli/ember-todomvc-tutorial/tree/master/steps/00-finished-todomvc/todomvc" rel="external nofollow">مستودع شيفرة تطبيقنا</a> للحصول عليها أو شاهد <a href="https://nullvoxpopuli.github.io/ember-todomvc-tutorial/" rel="external nofollow">الإصدار المباشر</a>.
</p>

<p>
	ادرس الشيفرة لمعرفة المزيد حول إطار عمل Ember، وراجع الفقرة التالية التي توفِّر مزيدًا من الموارد وبعض نصائح استكشاف الأخطاء وإصلاحها.
</p>

<h2>
	استكشاف الأخطاء العامة وإصلاحها وبنى gotcha والمفاهيم الخاطئة
</h2>

<p>
	كان آخر تحديث للقائمة التالية في شهر 6 من عام 2020.
</p>

<h3>
	كيف يمكنني تنقيح الأخطاء التي يظهرها إطار العمل؟
</h3>

<p>
	توجد <a href="https://guides.emberjs.com/release/ember-inspector/" rel="external nofollow">الإضافة <code>ember-inspector</code></a> بالنسبة للأشياء الخاصة بإطار العمل والتي تسمح بفحص ما يلي:
</p>

<ul>
<li>
		المسارات Routes والمتحكِّمات Controllers.
	</li>
	<li>
		المكوِنات.
	</li>
	<li>
		الخدمات.
	</li>
	<li>
		الوعود Promises.
	</li>
	<li>
		البيانات وهي بيانات من واجهة برمجة تطبيقات بعيدة مثل <code>ember-data</code> افتراضيًا.
	</li>
	<li>
		معلومات الإهمال Deprecation Information.
	</li>
	<li>
		تصيير الأداء.
	</li>
</ul>
<p>
	اطّلع على <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D8%B1-r665/" rel="">أدوات المطوِّر</a> التي تفيدك في تنقيح الأخطاء، وتعرّف على <a href="https://academy.hsoub.com/programming/workflow/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D9%90%D9%91%D8%B1-devtools-%D9%81%D9%8A-chrome-r554/" rel="">كيفية استخدام أدوات المطوِّر DevTools في كروم Chrome</a>.
</p>

<p>
	بوجد ملفان رئيسيان من ملفات جافاسكربت هما: vendor.js و ‎{app-name}.js في أيِّ مشروع Ember افتراضي، ويُنشَآن هذان الملفان باستخدام خرائط الشيفرة البرمجية Sourcemaps، لذلك ستُحمَّل خريطة الشيفرة البرمجية وستُوضَع نقطة التوقف في شيفرة مُترجَمة مسبقًا لتسهيل الارتباط بشيفرة مشروعك باستخدام منقّح أخطاء عند فتح الملف vendor.js أو الملف ‎{app-name}.js للبحث عن الشيفرة البرمجية ذات الصلة.
</p>

<h3>
	هل سأحتاج إلى واجهة ember-data المثبتة مسبقا؟
</h3>

<p>
	لن تحتاجها على الإطلاق، إذ تحل واجهة <code>ember-data</code> المشاكل الأكثر شيوعًا التي سيعمل بها أيّ تطبيق يتعامل مع البيانات بحيث يمكن تشغيل عميل بيانات واجهتك الأمامية، وهناك بديل شائع لأيّ عميل بيانات واجهة أمامية كامل الميزات هو <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84%D8%A7%D8%AA-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%84%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-fetch-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1297/" rel="">Fetch <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a>، إذ سيبدو المسار <code>Route</code> باستخدام <code>fetch()‎</code> كما يلي باستخدام أنماط التصميم التي يوفرها إطار العمل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_67" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">Route</span><span class="pln"> from </span><span class="str">'@ember/routing/route'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">MyRoute</span><span class="pln"> extends </span><span class="typ">Route</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  async model</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'some/url/to/json/data'</span><span class="pun">);</span><span class="pln">
    let json </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      data</span><span class="pun">:</span><span class="pln"> json
    </span><span class="pun">};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	لماذا لا يمكنني استخدام جافاسكربت فقط؟
</h3>

<p>
	هذا هو السؤال الأكثر شيوعًا الذي يسمعه مجتمع Ember من الأشخاص الذين لديهم خبرة سابقة في <a href="https://wiki.hsoub.com/React" rel="external">React</a>، إذ يمكن استخدام صيغة <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B5%D9%8A%D8%BA%D8%A9-javascript-syntax-extension-jsx-r777/" rel="">JSX</a> أو أيّ شكل آخر من أشكال إنشاء <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">نموذج DOM</a>، ولكن ليس هناك شيء قوي مثل نظام قوالب Ember، حيث يفرض الحد الأدنى منه قرارات معينة، ويسمح بشيفرة برمجية أكثر تناقسًا مع الحفاظ على القالب أكثر هيكلية بدلًا من ملئه بشيفرة برمجية حسب الرغبة.
</p>

<h3>
	ما هي حالة المساعد mut؟
</h3>

<p>
	انتقل المساعد <code>mut</code> مع إطار العمل Ember عند انتقاله من البيانات ذات الاتجاهين إلى مجرى البيانات الأكثر شيوعًا والأسهل ذي الاتجاه الواحد، إذ يمكن عدّ المساعد<code>mut</code> بمثابة تحويل وقت البناء الذي يغلّف وسيطه بتابع ضابط setter، إذ يسمح استخدام المساعد<code>mut</code> بالتصريح عن دوال إعدادات القالب فقط كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_70" style="">
<span class="pun">&lt;</span><span class="typ">Checkbox</span><span class="pln">
  </span><span class="lit">@value</span><span class="pun">={{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">someData</span><span class="pun">}}</span><span class="pln">
  </span><span class="lit">@onToggle</span><span class="pun">={{</span><span class="pln">fn </span><span class="pun">(</span><span class="pln">mut </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">someData</span><span class="pun">)</span><span class="pln"> </span><span class="pun">(</span><span class="pln">not </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">someData</span><span class="pun">)}}</span><span class="pln">
</span><span class="pun">/&gt;</span></pre>

<p>
	بينما ستكون هناك حاجة إلى صنف مكوّن بدون استخدام المساعد<code>mut</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_72" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> from </span><span class="str">'@glimmer/component'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> tracked </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@glimmer/tracking'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> action </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@ember/object'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Example</span><span class="pln"> extends </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">@tracked</span><span class="pln"> someData </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">

  </span><span class="lit">@action</span><span class="pln">
  setData</span><span class="pun">(</span><span class="pln">newValue</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">someData </span><span class="pun">=</span><span class="pln"> newValue</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيُستدعَى هذا الصنف بعد ذلك في القالب كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_74" style="">
<span class="pun">&lt;</span><span class="typ">Checkbox</span><span class="pln"> </span><span class="lit">@data</span><span class="pun">={{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">someData</span><span class="pun">}}</span><span class="pln"> </span><span class="lit">@onChange</span><span class="pun">={{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">setData</span><span class="pun">}}</span><span class="pln"> </span><span class="pun">/&gt;</span></pre>

<p>
	يمكن أن يكون استخدام <code>mut</code> مرغوبًا نظرًا لاختصاره، ولكنه يحتوي على دلالات غريبة يمكن أن تتسبّب في الكثير من الارتباك لمعرفة المقصود منها.
</p>

<p>
	كانت هناك بعض الأفكار الجديدة التي وُضِعت معًا في شكل إضافات تستخدِم واجهات برمجة تطبيقات <a href="https://github.com/pzuraq/ember-set-helper" rel="external nofollow"><code>ember-set-helper</code></a> و <a href="https://github.com/pzuraq/ember-box" rel="external nofollow"><code>ember-box</code></a>، ويحاول كلاهما حل مشاكل <code>mut</code> من خلال تقديم مفاهيم أوضح، وتجنب تحولات وقت البناء وسلوك آلة Glimmer الافتراضية الضمني.
</p>

<p>
	باستخدام <code>ember-set-helper</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_76" style="">
<span class="pun">&lt;</span><span class="typ">Checkbox</span><span class="pln">
  </span><span class="lit">@value</span><span class="pun">={{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">someData</span><span class="pun">}}</span><span class="pln">
  </span><span class="lit">@onToggle</span><span class="pun">={{</span><span class="kwd">set</span><span class="pln"> </span><span class="kwd">this</span><span class="pln"> </span><span class="str">"someData"</span><span class="pln"> </span><span class="pun">(</span><span class="pln">not </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">someData</span><span class="pun">)}}</span><span class="pln">
</span><span class="pun">/&gt;</span></pre>

<p>
	باستخدام <code>ember-box</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2362_78" style="">
<span class="pun">{{#</span><span class="pln">let </span><span class="pun">(</span><span class="pln">box </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">someData</span><span class="pun">)</span><span class="pln"> as </span><span class="pun">|</span><span class="pln">someData</span><span class="pun">|}}</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">Checkbox</span><span class="pln">
    </span><span class="lit">@value</span><span class="pun">={{</span><span class="pln">unwrap someData</span><span class="pun">}}</span><span class="pln">
    </span><span class="lit">@onToggle</span><span class="pun">={{</span><span class="pln">update someData </span><span class="pun">(</span><span class="pln">not </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">someData</span><span class="pun">)}}</span><span class="pln">
  </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">{{/</span><span class="pln">let</span><span class="pun">}}</span></pre>

<p>
	لا تُعَدّ أيّ من هذه الحلول شائعةً بين أعضاء المجتمع، ولا يزال الأشخاص يحاولون اكتشاف واجهة برمجة تطبيقات مريحة وبسيطة لضبط البيانات في سياق القالب فقط دون أصناف جافاسكربت الداعمة.
</p>

<h3>
	ما هو الغرض من المتحكمات؟
</h3>

<p>
	المتحكِّمات Controllers هي أنماط مفردة Singletons يمكن أن تساعد في إدارة تصيير السياق الخاص بالمسار النشط، وتعمل إلى حد كبير مثل أصناف جافاسكربت الداعمة للمكوّن، والمتحكِّمات هي -من شهر 1 عام 2020- الطريقة الوحيدة لإدارة محدّدات استعلام عناوين URL، إذ يجب أن تكون المتحكِّمات خفيفةً إلى حد ما في مسؤولياتها وفي تفويض المكوّنات والخدمات حيثما أمكن ذلك.
</p>

<h3>
	ما هو الغرض من المسارات؟
</h3>

<p>
	يمثِّل المسار Route جزءًا من عنوان URL عندما ينتقل المستخدِم من مكان إلى آخر في التطبيق، فالمسار له المسؤوليات التالية فقط:
</p>

<ul>
<li>
		تحميل الحد الأدنى من البيانات المطلوبة لتصيير المسار أو عرض الشجرة الفرعية.
	</li>
	<li>
		يُعَدّ بوابة الوصول إلى المسار وإعادة التوجيه إذا لزم الأمر.
	</li>
	<li>
		التعامل مع حالات التحميل والخطأ في الحد الأدنى من البيانات المطلوبة.
	</li>
</ul>
<p>
	يحتوي المسار على ثلاثة خطّافات لدورة الحياة فقط وجميعها اختيارية وهي:
</p>

<ul>
<li>
		<code>beforeModel</code>: بوابة الوصول إلى المسار.
	</li>
	<li>
		<code>model</code>: مكان تحميل البيانات.
	</li>
	<li>
		<code>afterModel</code>: للتحقق من الوصول.
	</li>
</ul>
<p>
	يتمتع المسار بالقدرة على التعامل مع الأحداث الشائعة الناتجة عن إعداد الخطاف <code>model</code>:
</p>

<ul>
<li>
		<code>loading</code>: ما يجب تطبيقه عند تحميل الخطّاف <code>model</code>.
	</li>
	<li>
		<code>error</code>: ما يجب فعله عند حدوث خطأ في الخطّاف <code>model</code>.
	</li>
</ul>
<p>
	يمكن لكل من <code>loading</code> و <code>error</code> تصيير القوالب الافتراضية بالإضافة إلى القوالب المخصَّصة المحددة في مكان آخر في التطبيق، مما يوحِّد حالات التحميل أو حالات الخطأ، كما يمكن العثور على مزيد من المعلومات حول <a href="https://api.emberjs.com/ember/release/classes/Route/" rel="external nofollow">ما يمكن للمسار تنفيذه في توثيق واجهة برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a>.
</p>

<p>
	أخيرًا، إذا احتجت إلى أي مساعدة، فأضف سؤالك ضمن قسم <a href="https://academy.hsoub.com/questions/c3-programming/" rel="">الأسئلة والأجوبة</a> في أكاديمية حسوب أو ضمن <a href="https://io.hsoub.com/programming" rel="external">مجتمع البرمجة</a> في حسوب IO وستصل إلى إجابتك من مجتمع المطورين العرب.
</p>

<p>
	ترجمة -وبتصرّف- للمقالين <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_routing" rel="external nofollow">Routing in Ember</a> و<a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_resources" rel="external nofollow">Ember resources and troubleshooting</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A7%D9%84%D8%AA%D9%81%D8%A7%D8%B9%D9%84-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-ember-%D9%88%D8%B8%D9%8A%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D8%B0%D9%8A%D9%8A%D9%84-%D9%88%D8%A7%D9%84%D8%B9%D8%B1%D8%B6-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A-r1653/" rel="">تنفيذ التفاعل في تطبيق Ember: وظيفة التذييل والعرض الشرطي</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-ember-r1648/" rel="">مقدمة إلى إطار العمل Ember</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/html/%D8%A8%D9%86%D9%8A%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-ember-%D9%88%D8%AA%D9%82%D8%B3%D9%8A%D9%85%D9%87%D8%A7-%D8%A5%D9%84%D9%89-%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-r1649/" rel="">بنية تطبيق إطار العمل Ember وتقسيمها إلى مكونات</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1663</guid><pubDate>Sat, 13 Aug 2022 10:59:47 +0000</pubDate></item><item><title>&#x62A;&#x646;&#x641;&#x64A;&#x630; &#x627;&#x644;&#x62A;&#x641;&#x627;&#x639;&#x644; &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642; Ember: &#x648;&#x638;&#x64A;&#x641;&#x629; &#x627;&#x644;&#x62A;&#x630;&#x64A;&#x64A;&#x644; &#x648;&#x627;&#x644;&#x639;&#x631;&#x636; &#x627;&#x644;&#x634;&#x631;&#x637;&#x64A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A7%D9%84%D8%AA%D9%81%D8%A7%D8%B9%D9%84-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-ember-%D9%88%D8%B8%D9%8A%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D8%B0%D9%8A%D9%8A%D9%84-%D9%88%D8%A7%D9%84%D8%B9%D8%B1%D8%B6-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A-r1653/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/62f3920e40206_---.png.c2fdc7e3f601333723ebfef75a3b8e24.png" /></p>

<p>
	حان الوقت الآن لمعالجة وظيفة التذييل Footer في تطبيقنا، إذ سنحدِّث عدّاد المهام لإظهار العدد الصحيح للمهام التي يجب إكمالها، وسنطبّق التنسيق بصورة صحيحة على المهام المكتملة من خلال تحديد مربع الاختيار، كما سنفعِّل زر "مسح المهام المكتملة Clear completed"، وسنتعرّف على استخدام التصيير أو العرض الشرطي Conditional Rendering في قوالبنا (التصيير والعرض والإخراج هي مترادفات لكلمة rendering).
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a>، ومعرفة استخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">سطر الأوامر أو الطرفية</a>، إذ يُعَدّ فهم ميزات جافاسكربت الحديثة مثل <a href="https://wiki.hsoub.com/JavaScript/class" rel="external">الأصناف Classes</a> و<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1286/" rel="">الوحدات Modules</a> وما إلى ذلك مفيدًا للغاية، لأن إطار العمل Ember يستخدمها بكثرة.
	</li>
	<li>
		<strong>الهدف</strong>: مواصلة تعلّم أصناف المكوّنات من خلال التعرّف على التصيير الشرطي وتفعيل بعض وظائف التذييل.
	</li>
</ul>
<h2>
	توصيل السلوك بالتذييل
</h2>

<p>
	يجب تطبيق الوظائف الثلاث التالية لكي يعمل التذييل:
</p>

<ul>
<li>
		عدّاد المهام المعلَّقة.
	</li>
	<li>
		مرشحات لجميع المهام والمهام النشطة والمهام المكتملة.
	</li>
	<li>
		زر لمسح المهام المكتملة.
	</li>
</ul>
<p>
	أولًا، يجب إنشاء صنف Class للتذييل بما أننا نحتاج إلى الوصول إلى الخدمة من مكوّن التذييل، لذلك أدخِل الأمر التالي في الطرفية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9471_9" style="">
<span class="pln">ember generate component</span><span class="pun">-</span><span class="kwd">class</span><span class="pln"> footer</span></pre>

<p>
	ثانيًا، ابحث بعد ذلك عن الملف todomvc/app/components/footer.js وعدّله إلى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9471_12" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> from </span><span class="str">'@glimmer/component'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> inject as service </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@ember/service'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">FooterComponent</span><span class="pln"> extends </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">@service</span><span class="pun">(</span><span class="str">'todo-data'</span><span class="pun">)</span><span class="pln"> todos</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ثالثًا، يجب الآن العودة إلى الملف todo-data.js وإضافة بعض الوظائف التي ستسمح بإعادة عدد المهام غير المكتملة لمعرفة عدد المهام المتبقية، ووظيفة مسح المهام المكتملة من القائمة التي يحتاجها زر "مسح المهام المكتملة Clear completed"، لذلك أضِف في الملف todo-data.js التابع الجالب Getter التالي بعد الجالب <code>all()‎</code> الموجود سابقًا لتحديد المهام غير المكتملة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9471_14" style="">
<span class="kwd">get</span><span class="pln"> incomplete</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">filterBy</span><span class="pun">(</span><span class="str">'isCompleted'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكننا باستخدام التابع <code>Array.Proxy.filterBy()‎</code> في Ember ترشيح الكائنات في المصفوفة بسهولة بناءً على شروط مساواةٍ بسيطة، إذ نريد في جزء الشيفرة السابق الحصول على جميع عناصر المهام عندما تكون الخاصية <code>isCompleted</code> مساوية للقيمة <code>false</code>، وسيُعاد حساب هذا التابع الجالب عندما تتغير قيمة الكائن في المصفوفة لأن الخاصية <code>isCompleted</code> مُميَّزة بالمزخرِف <code>‎@tracked</code> في الكائن <code>Todo</code>.
</p>

<p>
	رابعًا، أضف بعد ذلك ما يلي بعد الدالة <code>add(text)‎</code> الموجودة مسبقًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9471_16" style="">
<span class="lit">@action</span><span class="pln">
clearCompleted</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">incomplete</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُعَدّ ذلك رائعًا لمسح المهام، إذ نحتاج فقط إلى ضبط المصفوفة <code>todos</code> لتساوي قائمة المهام غير المكتملة.
</p>

<p>
	خامسًا، أخيرًا، يجب الاستفادة من هذه الوظيفة الجديدة في قالب footer.hbs، لذلك انتقل إلى هذا الملف الآن.
</p>

<p>
	سادسًا، ضَع أولًا مكان السطر التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9471_26" style="">
<span class="tag">&lt;strong&gt;</span><span class="pln">0</span><span class="tag">&lt;/strong&gt;</span><span class="pln"> todos left</span></pre>

<p>
	ما يلي، إذ يُملَأ عدد المهام غير المكتملة بطول المصفوفة <code>incomplete</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9471_24" style="">
<span class="tag">&lt;strong&gt;</span><span class="pln">{{this.todos.incomplete.length}}</span><span class="tag">&lt;/strong&gt;</span><span class="pln"> todos left</span></pre>

<p>
	سابعًا، ثم ضع مكان السطر التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9471_22" style="">
<span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"button"</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"clear-completed"</span><span class="tag">&gt;</span></pre>

<p>
	ما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9471_31" style="">
<span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"button"</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"clear-completed"</span><span class="pln"> {{</span><span class="atn">on</span><span class="pln"> </span><span class="atv">'click'</span><span class="pln"> </span><span class="atn">this</span><span class="pln">.</span><span class="atn">todos</span><span class="pln">.</span><span class="atn">clearCompleted</span><span class="pln">}}</span><span class="tag">&gt;</span></pre>

<p>
	إذا نقرتَ على الزر الآن، فسيُشغَّل الإجراء <code>clearCompleted()‎</code> الذي أضفناه سابقًا، ولكن إذا حاولت النقر على زر "مسح المهام المكتملة Clear Completed"، فلن يبدو التطبيق أنه يفعل أيّ شيء بسبب عدم وجود طريقة لإكمال المهام حاليًا، كما يجب توصيل القالب <code>todo.hbs</code> بالخدمة، بحيث يؤدي تحديد مربع الاختيار المتعلق به إلى تغيير حالة كل مهمة.
</p>

<h2>
	مشكلة كتابة todos بدلا من todo
</h2>

<p>
	لدينا مشكلة صغيرة أخرى نتعامل معها، إذ تشير العبارة "todos left" إلى وجود عدد من المهام المتبقية بالرغم من وجود مهمة واحدة متبقية أحيانًا، وهذا سيء قواعديًا.
</p>

<p>
	يمكن حل هذه المشكلة من خلال تعديل هذا الجزء من القالب ليحتوي على التصيير الشرطي، إذ يمكنك في Ember تصيير أجزاء من القالب شرطيًا باستخدام المحتوى الشرطي مثل الكتلة البسيطة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9471_33" style="">
<span class="pun">{{#</span><span class="kwd">if</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">thingIsTrue</span><span class="pun">}}</span><span class="pln">
  </span><span class="typ">Content</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> the block form of </span><span class="str">"if"</span><span class="pln">
</span><span class="pun">{{/</span><span class="kwd">if</span><span class="pun">}}</span></pre>

<p>
	ضع مكان الجزء التالي من footer.hbs:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9471_35" style="">
<span class="tag">&lt;strong&gt;</span><span class="pln">{{this.todos.incomplete.length}}</span><span class="tag">&lt;/strong&gt;</span><span class="pln"> todos left</span></pre>

<p>
	ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9471_37" style="">
<span class="pun">&lt;</span><span class="pln">strong</span><span class="pun">&gt;{{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">incomplete</span><span class="pun">.</span><span class="pln">length</span><span class="pun">}}&lt;/</span><span class="pln">strong</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">{{#</span><span class="kwd">if</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">incomplete</span><span class="pun">.</span><span class="pln">length </span><span class="pun">===</span><span class="pln"> </span><span class="lit">1</span><span class="pun">}}</span><span class="pln">
    todo
  </span><span class="pun">{{</span><span class="kwd">else</span><span class="pun">}}</span><span class="pln">
    todos
  </span><span class="pun">{{/</span><span class="kwd">if</span><span class="pun">}}</span><span class="pln">
    left</span></pre>

<p>
	سيؤدي ذلك إلى إعطاء خطأ، ولكن لا تستطيع عبارات <code>if</code> البسيطة هذه في Ember حاليًا اختبار تعبير معقد مثل الموازنة، وإنما تستطيع اختبار قيمة الصواب أو الخطأ فقط، لذلك يجب إضافة جالب getter إلى الملف todo-data.js لإعادة النتيجة <code>this.incomplete.length === 1</code> ثم استدعاؤها في القالب.
</p>

<p>
	أضف الجالب الجديد الآتي إلى الملف todo-data.js بعد التوابع الجالبة الموجودة مسبقًا مباشرةً، ولاحظ أننا نحتاج إلى <code>this.incomplete.length</code> وليس <code>this.todos.incomplete.length</code> لأننا نطبّق ذلك ضمن الخدمة حيث يتوفر الجالب <code>incomplete()‎</code> مباشرةً، كما أنّ محتويات الخدمة متوفرة في القالب مثل المهام <code>todos</code> عبر التعليمة <code>‎@service('todo-data') todos;‎</code> ضمن صنف التذييل، وبالتالي سيكون <code>this.todos.incomplete.length</code> هناك.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9471_39" style="">
<span class="kwd">get</span><span class="pln"> todoCountIsOne</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">incomplete</span><span class="pun">.</span><span class="pln">length </span><span class="pun">===</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ارجع بعد ذلك إلى footer.hbs وعدِّل قسم القالب السابق الذي عدّلناه إلى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9471_41" style="">
<span class="pun">&lt;</span><span class="pln">strong</span><span class="pun">&gt;{{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">incomplete</span><span class="pun">.</span><span class="pln">length</span><span class="pun">}}&lt;/</span><span class="pln">strong</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">{{#</span><span class="kwd">if</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">todoCountIsOne</span><span class="pun">}}</span><span class="pln">
    todo
  </span><span class="pun">{{</span><span class="kwd">else</span><span class="pun">}}</span><span class="pln">
    todos
  </span><span class="pun">{{/</span><span class="kwd">if</span><span class="pun">}}</span><span class="pln">
    left</span></pre>

<p>
	احفظ الملف واختبره، وسترى الكلمة الصحيحة المُستخدَمة عندما يكون لديك عنصر واحد لتنفيذه.
</p>

<p>
	لاحظ صيغة كتلة <code>if</code> في Ember، ويمكنك استخدام الشكل المضمَّن التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9471_43" style="">
<span class="pun">{{</span><span class="kwd">if</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">todoCountIsOne </span><span class="str">"todo"</span><span class="pln"> </span><span class="str">"todos"</span><span class="pun">}}</span></pre>

<h2>
	استكمال المهام
</h2>

<p>
	يجب استخدام صنف للوصول إلى الخدمة كما هو الحال مع المكونات الأخرى.
</p>

<h3>
	إنشاء الصنف todo
</h3>

<p>
	أولًا، شغّل الأمر التالي في الطرفية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9471_45" style="">
<span class="pln">ember generate component</span><span class="pun">-</span><span class="kwd">class</span><span class="pln"> todo</span></pre>

<p>
	ثانيًا، انتقل الآن إلى الملف todomvc/app/components/todo.js وعدّل محتوياته لتبدو كما يلي لمنح المكوّن <code>todo</code> إمكانية الوصول إلى الخدمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9471_47" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> from </span><span class="str">'@glimmer/component'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> inject as service </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@ember/service'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">TodoComponent</span><span class="pln"> extends </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">@service</span><span class="pun">(</span><span class="str">'todo-data'</span><span class="pun">)</span><span class="pln"> todos</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ثالثًا، ارجع مرةً أخرى إلى ملف الخدمة todo-data.js وأضف الإجراء التالي بعد الإجراءات السابقة مباشرةً، مما سيسمح بتبديل حالة الاكتمال لكل مهمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9471_49" style="">
<span class="lit">@action</span><span class="pln">
toggleCompletion</span><span class="pun">(</span><span class="pln">todo</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  todo</span><span class="pun">.</span><span class="pln">isCompleted </span><span class="pun">=</span><span class="pln"> </span><span class="pun">!</span><span class="pln">todo</span><span class="pun">.</span><span class="pln">isCompleted</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	تحديث القالب لإظهار الحالة المكتملة
</h3>

<p>
	أخيرًا، سنعدّل القالب todo.hbs بحيث ترتبط قيمة مربع الاختيار بالخاصية <code>isCompleted</code> في المهمة، حيث يُستدعَى التابع <code>toggleCompletion()‎</code> في خدمة المهمة عند التعديل.
</p>

<p>
	أولًا، ابحث أولًا عن السطر التالي في الملف todo.hbs:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9471_51" style="">
<span class="tag">&lt;li&gt;</span></pre>

<p>
	وضَع مكانه ما يلي، إذ ستلاحظ أننا نستخدم المحتوى الشرطي لإضافة قيمة الصنف إذا كان ذلك مناسبًا:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9471_53" style="">
<span class="tag">&lt;li</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"{{ if @todo.isCompleted 'completed' }}"</span><span class="tag">&gt;</span></pre>

<p>
	ثانيًا، ابحث بعد ذلك عمّا يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9471_55" style="">
<span class="tag">&lt;input</span><span class="pln">
  </span><span class="atn">aria-label</span><span class="pun">=</span><span class="atv">"Toggle the completion of this todo"</span><span class="pln">
  </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"toggle"</span><span class="pln">
  </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"checkbox"</span><span class="pln">
</span><span class="tag">&gt;</span></pre>

<p>
	وضَع مكانه ما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9471_57" style="">
<span class="tag">&lt;input</span><span class="pln">
  </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"toggle"</span><span class="pln">
  </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"checkbox"</span><span class="pln">
  </span><span class="atn">aria-label</span><span class="pun">=</span><span class="atv">"Toggle the completion of this todo"</span><span class="pln">
  </span><span class="atn">checked</span><span class="pun">=</span><span class="atv">{{</span><span class="pln"> @</span><span class="atn">todo</span><span class="pln">.</span><span class="atn">isCompleted</span><span class="pln"> }}
  {{ </span><span class="atn">on</span><span class="pln"> </span><span class="atv">'change'</span><span class="pln"> (</span><span class="atn">fn</span><span class="pln"> </span><span class="atn">this</span><span class="pln">.</span><span class="atn">todos</span><span class="pln">.</span><span class="atn">toggleCompletion</span><span class="pln"> @</span><span class="atn">todo</span><span class="pln">) }}
</span><span class="tag">&gt;</span></pre>

<p>
	<strong>ملاحظة</strong>: يستخدِم جزء الشيفرة السابق كلمةً مفتاحيةً جديدةً خاصةً بإطار عمل Ember هي <code>fn</code> التي تسمح بالتطبيق الجزئي Partial Application، وهو مشابه للتابع <code>bind</code> لكنه لا يغير سياق الاستدعاء، ويكافئ استخدام التابع <code>bind</code> مع الوسيط الأول <code>null</code>.
</p>

<p>
	أعِد تشغيل <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">خادم التطوير</a> وانتقل إلى المضيف المحلي <code>localhost:4200</code> مرةً أخرى، وسترى أنه لدينا عدّاد "المهام المتبقية todos left" وزر "المسح Clear":
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="104816" href="https://academy.hsoub.com/uploads/monthly_2022_08/01_todos-being-marked-completed-and-cleared.gif.43a35c36393429dd1b4ddfcfecca61e0.gif" rel=""><img alt="01_todos-being-marked-completed-and-cleared.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="104816" data-unique="a82risefd" src="https://academy.hsoub.com/uploads/monthly_2022_08/01_todos-being-marked-completed-and-cleared.thumb.gif.19250ec068124fe05af188f45acb9c93.gif" style="width: 580px; height: auto;"></a>
</p>

<p>
	يمكن أن تسأل نفسك لماذا لا نطبّق التبديل على المكوِّن فقط؟ نظرًا لأن الدالة قائمة بذاتها ولا تحتاج على الإطلاق إلى أيّ شيء من الخدمة، وبما أننا في النهاية سنرغب في الاستمرار أو مزامنة جميع تغييرات قائمة المهام مع التخزين المحلي (اطّلع على <a href="https://nullvoxpopuli.github.io/ember-todomvc-tutorial/" rel="external nofollow">الإصدار الأخير من التطبيق</a>)، فستكون جميع عمليات تغيير الحالة المستمرة في المكان نفسه.
</p>

<h2>
	الخلاصة
</h2>

<p>
	يمكننا الآن وضع علامة على المهام المكتملة ومسحها أيضًا، والشيء الوحيد المتبقي لتفعيل التذييل هو عمليات ترشيح "جميع المهام All" و"المهام النشطة Active" و"المهام المكتملة Completed"، إذ سنطبّق ذلك في المقال التالي باستخدام التوجيه Routing.
</p>

<p>
	ترجمة -وبتصرّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_conditional_footer" rel="external nofollow">Ember Interactivity: Footer functionality, conditional rendering</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A7%D9%84%D8%AA%D9%81%D8%A7%D8%B9%D9%84-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-ember-%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D9%88%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%88%D8%A7%D9%84%D8%AD%D8%A7%D9%84%D8%A9-r1652/" rel="">تنفيذ التفاعل في تطبيق Ember: الأحداث والأصناف والحالة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/html/%D8%A8%D9%86%D9%8A%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-ember-%D9%88%D8%AA%D9%82%D8%B3%D9%8A%D9%85%D9%87%D8%A7-%D8%A5%D9%84%D9%89-%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-r1649/" rel="">بنية تطبيق إطار العمل Ember وتقسيمها إلى مكونات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-ember-r1648/" rel="">مقدمة إلى إطار العمل Ember</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1653</guid><pubDate>Wed, 10 Aug 2022 11:24:35 +0000</pubDate></item><item><title>&#x62A;&#x646;&#x641;&#x64A;&#x630; &#x627;&#x644;&#x62A;&#x641;&#x627;&#x639;&#x644; &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642; Ember: &#x627;&#x644;&#x623;&#x62D;&#x62F;&#x627;&#x62B; &#x648;&#x627;&#x644;&#x623;&#x635;&#x646;&#x627;&#x641; &#x648;&#x627;&#x644;&#x62D;&#x627;&#x644;&#x629;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A7%D9%84%D8%AA%D9%81%D8%A7%D8%B9%D9%84-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-ember-%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D9%88%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%88%D8%A7%D9%84%D8%AD%D8%A7%D9%84%D8%A9-r1652/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/62f38c814c478_----Ember.png.04e529097e256f3e137851f279d6b8fd.png" /></p>

<p>
	سنبدأ في هذا المقال بإضافة بعض التفاعل إلى تطبيقنا مما يوفر القدرة على إضافة وعرض عناصر مهام جديدة، كما سنتعرّف على استخدام الأحداث في إطار العمل Ember، وإنشاء أصناف مكوّنات لتحتوي على <a href="https://academy.hsoub.com/programming/javascript/%D9%86%D9%85%D8%B7-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r785/" rel="">شيفرة جافاسكربت JavaScript</a> للتحكم بالميزات التفاعلية وإعداد خدمة لتتبّع حالة بيانات تطبيقنا.
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a>، ومعرفة استخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">سطر الأوامر أو الطرفية</a>، إذ يُعَدّ فهم ميزات جافاسكربت الحديثة مثل <a href="https://wiki.hsoub.com/JavaScript/class" rel="external">الأصناف Classes</a>، و<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1286/" rel="">الوحدات Modules</a>، وما إلى ذلك مفيدًا للغاية لأن إطار العمل Ember يستخدمها بكثرة.
	</li>
	<li>
		<strong>الهدف</strong>: معرفة كيفية إنشاء أصناف المكوّنات واستخدام الأحداث للتحكم في التفاعل وتتبع حالة التطبيق باستخدام خدمة.
	</li>
</ul>
<h2>
	إضافة التفاعل
</h2>

<p>
	لدينا الآن نسخة من تطبيق قائمة المهام todo app مقسَّمة إلى مكوّنات ومُعاد بناؤها، ولنتعرَّف على كيفية إضافة التفاعل الذي نحتاجه ليعمل التطبيق، إذ يجب التصريح عن أهداف ومسؤوليات كل مكوّن عند البدء في التفكير في التفاعل كما سنوضّح فيما يلي، ثم سنوجّهك لتتعلم كيفية تنفيذ ذلك.
</p>

<h2>
	إنشاء المهام
</h2>

<p>
	نريد أن نتمكن من إرسال المهمة المكتوبة في حقل إدخال المهام عندما نضغط على مفتاح <code>Enter</code> لتظهر في قائمة المهام، ويجب أن نكون قادرين على التقاط النص المكتوب في حقل الإدخال لتعرف شيفرة جافاسكربت ما كتبناه، كما يمكننا حفظ مهامنا وتمرير هذا النص إلى مكوّن قائمة المهام لعرضه، ويمكننا التقاط الحدث <code>keydown</code> باستخدام المُعدِّل <code>on</code> الذي يُعَدّ صيغةً مبسَّطةً في Ember للتابعَين <code>addEventListener</code> و <code>removeEventListener</code>.
</p>

<p>
	أضف السطر الجديد الموضَّح أدناه إلى الملف header.hbs:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_7" style="">
<span class="pun">&lt;</span><span class="pln">input
  </span><span class="kwd">class</span><span class="pun">=</span><span class="str">'new-todo'</span><span class="pln">
  aria</span><span class="pun">-</span><span class="pln">label</span><span class="pun">=</span><span class="str">'What needs to be done?'</span><span class="pln">
  placeholder</span><span class="pun">=</span><span class="str">'What needs to be done?'</span><span class="pln">
  autofocus
  </span><span class="pun">{{</span><span class="pln">on </span><span class="str">'keydown'</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">onKeyDown</span><span class="pun">}}</span><span class="pln">
</span><span class="pun">&gt;</span></pre>

<p>
	توضَع هذه السمة الجديدة بين أقواس مزدوجة معقوصة، ويدل ذلك على أنها جزء من صيغة قالب آلي في إطار عمل Ember، فالوسيط الأول المُمرَّر إلى <code>on</code> هو نوع الحدث الذي يجب الاستجابة له <code>keydown</code>، والوسيط الأخير هو معالِج الحدث أي الشيفرة المُشغَّلة استجابةً لإطلاق الحدث <code>keydown</code>، في حين تشير الكلمة المفتاحية <code>this</code> إلى سياق Context أو نطاق Scope المكوّن الذي سيختلف بين مكوّن وآخر.
</p>

<p>
	يمكننا تحديد ما هو متاح ضمن <code>this</code> من خلال إنشاء صنف مكوّن ليتماشى مع مكوّنك، وهو صنف بلغة جافاسكربت الصرفة Vanilla JavaScript وليس له معنًى خاصًا في إطار عمل Ember باستثناء وراثة أو توسعة الصنف الأب <code>Component</code>.
</p>

<p>
	يمكنك إنشاء الصنف <code>header</code> ليتوافق مع مكوّن الترويسة من خلال كتابة الأمر التالي في الطرفية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_13" style="">
<span class="pln">ember generate component</span><span class="pun">-</span><span class="kwd">class</span><span class="pln"> header</span></pre>

<p>
	مما يؤدي إلى إنشاء ملف الصنف الفارغ التالي في الملف todomvc/app/components/header.js:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_15" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> from </span><span class="str">'@glimmer/component'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">HeaderComponent</span><span class="pln"> extends </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	سننفّذ في هذا الملف شيفرة معالج الحدث، لذلك عدّل محتواه إلى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_17" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> from </span><span class="str">'@glimmer/component'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> action </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@ember/object'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">HeaderComponent</span><span class="pln"> extends </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="lit">@action</span><span class="pln">
  onKeyDown</span><span class="pun">({</span><span class="pln"> target</span><span class="pun">,</span><span class="pln"> key </span><span class="pun">})</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let text </span><span class="pun">=</span><span class="pln"> target</span><span class="pun">.</span><span class="pln">value</span><span class="pun">.</span><span class="pln">trim</span><span class="pun">();</span><span class="pln">
    let hasValue </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Boolean</span><span class="pun">(</span><span class="pln">text</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">key </span><span class="pun">===</span><span class="pln"> </span><span class="str">'Enter'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> hasValue</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      alert</span><span class="pun">(</span><span class="pln">text</span><span class="pun">);</span><span class="pln">

      target</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُعَدّ المزخرِف <code>‎@action</code> الشيفرة الوحيدة الخاصة بإطار العمل Ember هنا بغض النظر عن وراثة الصنف الأب <code>Component</code> والعناصر الخاصة بإطار العمل Ember التي نستوردها باستخدام صيغة وحدة جافاسكربت، بينما باقي الملف مكتوب بلغة جافاسكربت الصرفة Vanilla JavaScript ويمكن أن يعمل في أيّ تطبيق آخر، كما يصرِّح المزخرِف <code>‎@action</code> عن أن الدالة هي إجراء action، مما يعني أنها نوع من الدوال التي ستُستدعَى من حدث وقع في القالب، كما أنّ <code>‎@action</code> يربط <code>this</code> الخاص بالدالة بنسخة من الصنف.
</p>

<p>
	<strong>ملاحظة</strong>: يُعَدّ المزخرِف Decorator دالة تغليف تغلِّف وتستدعي دوالًا أو خاصيات أخرى مما يوفر وظائفًا إضافيةً، إذ يشغّل المزخرِف <code>‎@tracked</code> مثلًا الشيفرة المُطبَّقة عليه، ويتتبّعه ويحدّث التطبيق تلقائيًا عند تغيير القيم.
</p>

<p>
	عُد إلى تبويب متصفحك مع تشغيل التطبيق، إذ يمكننا كتابة ما نريد وستظهر رسالة تنبيه تخبرنا بما كتبناه بالضبط عندما نضغط على مفتاح <code>Enter</code>.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="104809" href="https://academy.hsoub.com/uploads/monthly_2022_08/01_todos-hello-there-alert.png.2f7b1f8bc7481ac1235d53151ed5a88f.png" rel=""><img alt="01_todos-hello-there-alert.png" class="ipsImage ipsImage_thumbnailed" data-fileid="104809" data-unique="49iuw6rfu" src="https://academy.hsoub.com/uploads/monthly_2022_08/01_todos-hello-there-alert.thumb.png.d1e1ab261d4b02528a08ec373bd26127.png" style="width: 500px; height: auto;"></a>
</p>

<p>
	نحتاج الآن لمكان لتخزين المهام لتتمكن المكوّنات الأخرى من الوصول إليها.
</p>

<h2>
	تخزين المهام باستخدام خدمة
</h2>

<p>
	يملك إطار عمل Ember إدارة حالة مبنية مسبقًا على مستوى التطبيق يمكننا استخدامها لإدارة تخزين مهامنا، والسماح لكل مكوّن من مكوّناتنا الوصول إلى البيانات من تلك الحالة على مستوى التطبيق، إذ يستدعي إطار Ember خدمات البناء التي تبقى قيد التشغيل طوال عُمر الصفحة، إذ سيؤدي تحديث الصفحة إلى مسحها.
</p>

<p>
	شغّل الأمر التالي في الطرفية لإنشاء خدمة لتخزين بيانات قائمة المهام:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_20" style="">
<span class="pln">ember generate service todo</span><span class="pun">-</span><span class="pln">data</span></pre>

<p>
	يعطي تشغيل الأمر السابق خرجًا يشبه الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_22" style="">
<span class="pln">installing service
  create app</span><span class="pun">/</span><span class="pln">services</span><span class="pun">/</span><span class="pln">todo</span><span class="pun">-</span><span class="pln">data</span><span class="pun">.</span><span class="pln">js
installing service</span><span class="pun">-</span><span class="pln">test
  create tests</span><span class="pun">/</span><span class="pln">unit</span><span class="pun">/</span><span class="pln">services</span><span class="pun">/</span><span class="pln">todo</span><span class="pun">-</span><span class="pln">data</span><span class="pun">-</span><span class="pln">test</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	مما يؤدي إلى إنشاء الملف todo-data.js ضمن المجلد todomvc/app/services لاحتواء خدمتنا، كما يحتوي هذا الملف في البداية على عبارة استيراد وصنف فارغ كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_24" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">Service</span><span class="pln"> from </span><span class="str">'@ember/service'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">TodoDataService</span><span class="pln"> extends </span><span class="typ">Service</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	يجب أولًا تتبّع كل من نص المهمة وما إذا كانت مكتملة أم لا، لذا أضف تعليمة الاستيراد التالية بعد تعليمة الاستيراد الموجودة مسبقًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_26" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> tracked </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@glimmer/tracking'</span><span class="pun">;</span></pre>

<p>
	أضف الصنف التالي بعد السطر السابق الذي أضفته:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_28" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Todo</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">@tracked</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">
  </span><span class="lit">@tracked</span><span class="pln"> isCompleted </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">

  constructor</span><span class="pun">(</span><span class="pln">text</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">text </span><span class="pun">=</span><span class="pln"> text</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمثل الصنف السابق مهمَّة، لأنه يحتوي على الخاصية <code>‎@tracked text</code> التي تحتوي على نص المهمَّة، ويحتوي على الخاصية <code>‎@tracked isCompleted</code> التي تحدِّد ما إذا كانت المهام مكتملةً أم لا، فإذا أنشأنا نسخةً من هذا الصنف، فسيكون للكائن <code>Todo</code> قيمة <code>text</code> أولية تساوي النص المعطَى له عند إنشائه، وتُعطَى الخاصية <code>isCompleted</code> القيمة <code>false</code>.
</p>

<p>
	الجزء الوحيد الخاص بإطار عمل Ember من هذا الصنف هو المزخرِف <code>‎@tracked</code> الذي يرتبط بنظام التفاعل ويسمح لإطار Ember بتحديث ما تراه في تطبيقك تلقائيًا إذا تغيرت الخاصيات المُتتبَّعة tracked، وحان الوقت الآن لإضافة شيء ما إلى جسم الخدمة، لذا أضِف أولًا تعليمة استيراد <code>import</code> أخرى بعد التعليمة السابقة لإتاحة الإجراءات actions ضمن الخدمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_30" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> action </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@ember/object'</span><span class="pun">;</span></pre>

<p>
	عدّل الكتلة <code>export default class TodoDataService extends Service { … }‎</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_32" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">TodoDataService</span><span class="pln"> extends </span><span class="typ">Service</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">@tracked</span><span class="pln"> todos </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">

  </span><span class="lit">@action</span><span class="pln">
  add</span><span class="pun">(</span><span class="pln">text</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let newTodo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todo</span><span class="pun">(</span><span class="pln">text</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[...</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">,</span><span class="pln"> newTodo</span><span class="pun">];</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستحتفظ الخاصية <code>todos</code> في الخدمة بقائمة مهامنا الموجودة ضمن مصفوفة، وسنميّزها بالمزخرِف <code>‎@tracked</code> لأننا نريد تحديث واجهة المستخدِم أيضًا عند تحديث قيمة الخاصية <code>todos</code>.
</p>

<p>
	يُضاف المزخرِف <code>‎@action</code> إلى الدالة <code>add()‎</code> التي يستدعيها القالب لربطها بنسخة الصنف. يمكن أن يكون ذلك مألوفًا في لغة جافاسكربت، ولكن لاحظ استدعاء التابع <code>pushObject()‎</code> في المصفوفة <code>todos</code>، والسبب أن إطار عمل Ember يوسّع نموذج المصفوفات في لغة جافاسكربت افتراضيًا، مما يمنحنا توابعًا ملائمة لضمان معرفة نظام التعقّب في إطار Ember بهذه التغييرات. هناك العشرات من هذه التوابع مثل <code>pushObject()‎</code> أو <code>insertAt()‎</code> أو <code>popObject()‎</code> التي يمكن استخدامها مع أيّ نوع وليس مع الكائنات فقط. يمنحنا التابع ArrayProxy الخاص بإطار عمل Ember توابعًا سهلة الاستخدام مثل <code>isAny()‎</code> و<code>findBy()‎</code> و<code>filterBy()‎</code> لتسهيل الأمور.
</p>

<h2>
	استخدام الخدمة من مكون الترويسة
</h2>

<p>
	يمكننا الآن بعد أن تحديد طريقة لإضافة المهام التفاعل مع هذه الخدمة من مكوّن الإدخال في الملف header.js لبدء إضافتها فعليًا، إذ يجب أولًا حقن الخدمة في القالب باستخدام المزخرِف <code>‎@inject</code> الذي سنعيد تسميته إلى <code>‎@service</code>.
</p>

<p>
	أضف تعليمة الاستيراد التالية إلى الملف header.js بعد تعليمتَي الاستيراد الموجودتَين مسبقًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_34" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> inject as service </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@ember/service'</span><span class="pun">;</span></pre>

<p>
	يمكننا الآن توفير الخدمة <code>todo-data</code> ضمن الصنف <code>HeaderComponent</code> عبر الكائن <code>todos</code> باستخدام المزخرِف <code>‎@service</code>، لذلك أضف السطر التالي بعد سطر التصدير <code>export‎</code> الأول مباشرةً:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_36" style="">
<span class="lit">@service</span><span class="pun">(</span><span class="str">'todo-data'</span><span class="pun">)</span><span class="pln"> todos</span><span class="pun">;</span></pre>

<p>
	يمكن الآن استبدال سطر النص البديل <code>alert(text);‎</code> باستدعاء الدالة <code>add()‎</code> الجديدة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_38" style="">
<span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">text</span><span class="pun">);</span></pre>

<p>
	إذا جربنا ذلك في تطبيق المهام في متصفحنا من خلال كتابة الأمر <code>npm start</code> ثم الانتقال إلى المضيف المحلي <code>localhost:4200</code>، فلن يحدث أيّ شيء بعد الضغط على مفتاح <code>Enter</code>، إذ يُعَدّ بناء التطبيق بدون أيّ أخطاء علامةً جيدةً، ولكن يمكننا رؤية إضافة مهامنا باستخدام الفاحص Ember Inspector كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="104810" href="https://academy.hsoub.com/uploads/monthly_2022_08/02_todos-in-ember-inspector.gif.c6052b9f7ebd277a9c86695b08692818.gif" rel=""><img alt="02_todos-in-ember-inspector.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="104810" data-unique="7q2qx3hp4" src="https://academy.hsoub.com/uploads/monthly_2022_08/02_todos-in-ember-inspector.thumb.gif.f0fc58973bacebe17a9fff3510bb558e.gif" style="width: 600px; height: auto;"></a>
</p>

<h2>
	عرض المهام
</h2>

<p>
	يجب أن تكون هناك طريقة لوضع المهام التي ننشئها فعليًا مكان مهامنا الساكنة "Buy Movie Tickets"، إذ يجب في المكوّن <code>TodoList</code> إخراج المهام من الخدمة وتصيير Render المكوّن <code>Todo</code> لكل مهمة، كما يمكن استعادة المهام من الخدمة، ولكن يحتاج المكوّن <code>TodoList</code> أولًا إلى صنف داعم للمكوّن لاحتواء هذه الوظيفة، لذلك اضغط على الاختصار Ctrl + C لإيقاف خادم التطوير وأدخِل الأمر التالي في الطرفية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_42" style="">
<span class="pln">ember generate component</span><span class="pun">-</span><span class="kwd">class</span><span class="pln"> todo</span><span class="pun">-</span><span class="pln">list</span></pre>

<p>
	يؤدي ذلك إلى إنشاء صنف المكوّن الجديد في المجلد todomvc/app/components/todo-list.js.
</p>

<p>
	املأ هذا الملف بالشيفرة التالية بحيث يمكن لقالبنا الوصول إلى الخدمة <code>todo-data</code> باستخدام الخاصية <code>todos</code>، ويمكن الوصول إليها باستخدام <code>this.todos</code> ضمن كل من الصنف والقالب:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_44" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> from </span><span class="str">'@glimmer/component'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> inject as service </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@ember/service'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">TodoListComponent</span><span class="pln"> extends </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">@service</span><span class="pun">(</span><span class="str">'todo-data'</span><span class="pun">)</span><span class="pln"> todos</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تتمثّل إحدى المشاكل هنا في أن الخدمة تسمَّى <code>todos</code> وتُسمَّى قائمة المهام <code>todos</code> أيضًا، لذلك يمكننا حاليًا الوصول إلى البيانات باستخدام <code>this.todos.todos</code>. لا يُعَد هذا أمرًا سهلًا، لذلك سنضيف تابعًا جالبًا getter إلى الخدمة <code>this.todos.todos</code> بالاسم <code>all</code> سيمثِّل جميع المهام.
</p>

<p>
	ارجع إلى الملف todo-data.js وأضِف ما يلي بعد التعليمة <code>‎@tracked todos = [];‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_46" style="">
<span class="kwd">get</span><span class="pln"> all</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكننا الآن الوصول إلى البيانات باستخدام <code>this.todos.all</code>، إذ يُعَدّ ذلك أسهل. انتقل إلى المكوّن <code>todo-list.hbs</code>، وضع مكان استدعاءات المكوّن الساكنة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_48" style="">
<span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> </span><span class="pun">/&gt;</span></pre>

<p>
	كتلة <code>‎#each</code> الآلية، وهي صيغة مُبسطَّة من تابع جافاسكربت <code>forEach()‎</code>، إذ تنشئ كتلة <code>‎#each</code> المكوّنَ <code>&lt;Todo /‎&gt;</code> لكل مهمة متوفرة في قائمة المهام التي يعيدها التابع الجالب <code>all()‎</code> الخاص بالخدمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_50" style="">
<span class="pun">{{#</span><span class="pln">each </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">all as </span><span class="pun">|</span><span class="pln">todo</span><span class="pun">|}}</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> </span><span class="lit">@todo</span><span class="pun">={{</span><span class="pln">todo</span><span class="pun">}}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">{{/</span><span class="pln">each</span><span class="pun">}}</span></pre>

<p>
	لنتعرّف على محتويات الشيفرة السابقة:
</p>

<ul>
<li>
		<code>this</code>: سياق التصيير أو نسخة المكوّن.
	</li>
	<li>
		<code>todos</code>: خاصية <code>this</code> التي عرّفناها في المكوّن <code>todo-list.js</code> باستخدام التعليمة <code>‎@service('todo-data') todos;‎</code>، وهي مرجع إلى الخدمة <code>todo-data</code>، مما يسمح بالتفاعل مع نسخة الخدمة مباشرةً.
	</li>
	<li>
		<code>all</code>: جالب الخدمة <code>todo-data</code> الذي يعيد جميع المهام.
	</li>
</ul>
<p>
	جرّب تشغيل الخادم مرةً أخرى وانتقل إلى التطبيق، وستجده يعمل، ولكن كلما أدخلت عنصر مهمة جديد، فسيظهر عنصر قائمة جديد تحت حقل إدخال النص، وستظهر العبارة "Buy Movie Tickets" دائمًا للأسف، لأن عنوان النص في كل عنصر قائمة مضمَّنٌ في ذلك النص كما هو موضَّح في الملف todo.hbs:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_52" style="">
<span class="pun">&lt;</span><span class="pln">label</span><span class="pun">&gt;</span><span class="typ">Buy</span><span class="pln"> </span><span class="typ">Movie</span><span class="pln"> </span><span class="typ">Tickets</span><span class="pun">&lt;/</span><span class="pln">label</span><span class="pun">&gt;</span></pre>

<p>
	عدِّل السطر السابق كما يلي لاستخدام الوسيط <code>‎@todo</code> الذي سيمثل المهمة التي مررناها إلى المكوّن عند استدعائه ضمن الملف todo-list.hbs في السطر <code>&lt;Todo @todo={{todo}} /‎&gt;</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9857_54" style="">
<span class="pun">&lt;</span><span class="pln">label</span><span class="pun">&gt;{{</span><span class="lit">@todo</span><span class="pun">.</span><span class="pln">text</span><span class="pun">}}&lt;/</span><span class="pln">label</span><span class="pun">&gt;</span></pre>

<p>
	جرّبه مرةً أخرى، ويجب أن تجد الآن أنّ النص المُرسَل من حقل الإدخال <code>&lt;input&gt;</code> يظهر بصورة صحيحة في واجهة المستخدِم كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="104811" href="https://academy.hsoub.com/uploads/monthly_2022_08/03_todos-being-appended-with-correct-text.gif.d689e9cb493282a663f6e427d02cc4c1.gif" rel=""><img alt="03_todos-being-appended-with-correct-text.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="104811" data-unique="9o2va6faw" src="https://academy.hsoub.com/uploads/monthly_2022_08/03_todos-being-appended-with-correct-text.thumb.gif.ae78d3f39d9dbcffdc440b9b1f1fd7d5.gif" style="width: 550px; height: auto;"></a>
</p>

<h2>
	الخلاصة
</h2>

<p>
	يمكننا الآن إضافة عناصر المهمات إلى تطبيقنا، كما يمكن تتبّع حالة البيانات باستخدام الخدمة، وسننتقل في المقال التالي إلى تشغيل وظائف التذييل Footer بما في ذلك عدّاد المهام، كما سنتعرّف على التصيير الشرطي وتصميم المهام بصورة صحيحة عند تحديدها، وسنفعّل زر "مسح المهام المكتملة Clear completed".
</p>

<p>
	ترجمة -وبتصرّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_interactivity_events_state" rel="external nofollow">Ember interactivity: Events, classes and state</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/html/%D8%A8%D9%86%D9%8A%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-ember-%D9%88%D8%AA%D9%82%D8%B3%D9%8A%D9%85%D9%87%D8%A7-%D8%A5%D9%84%D9%89-%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-r1649/" rel="">بنية تطبيق إطار العمل Ember وتقسيمها إلى مكونات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-ember-r1648/" rel="">مقدمة إلى إطار العمل Ember</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1652</guid><pubDate>Wed, 10 Aug 2022 11:07:31 +0000</pubDate></item><item><title>&#x645;&#x642;&#x62F;&#x645;&#x629; &#x625;&#x644;&#x649; &#x625;&#x637;&#x627;&#x631; &#x627;&#x644;&#x639;&#x645;&#x644; Ember</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-ember-r1648/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/62ee134d6a8e3_-----Ember.png.98941e906215b7faf08cc89c2bfe9bf7.png" /></p>

<p>
	سنلقي نظرةً في هذا المقال على إطار العمل Ember وآلية عمله وفوائده، وكيفية تثبيت سلسلة أدواته محليًا وإنشاء تطبيق نموذجي ثم إجراء إعداد أولي لتجهيز التطبيق للتطوير.
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a>، ومعرفة استخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">سطر الأوامر أو الطرفية</a>، كما يُعَدّ فهم ميزات <a href="https://academy.hsoub.com/programming/javascript/%d9%85%d8%a7-%d9%87%d9%8a-%d8%ac%d8%a7%d9%81%d8%a7-%d8%b3%d9%83%d8%b1%d9%8a%d8%a8%d8%aa-%d8%9f-r524/" rel="">جافاسكربت</a> الحديثة مثل الأصناف Classes والوحدات Modules وما إلى ذلك مفيدًا للغاية، لأن إطار العمل Ember يستخدِمها بكثرة.
	</li>
	<li>
		<strong>الهدف</strong>: معرفة كيفية تثبيت إطار عمل Ember وإنشاء تطبيق بسيط.
	</li>
</ul>
<p>
	يُعَدّ Ember إطار عمل من النوع خدمة-مكونات Component-service يركّز على عملية تطوير تطبيقات الويب وإضفاء تجربة مميزة على واجهاتها ويقلل من الاختلافات بين التطبيقات، ويُعَدّ طبقةً حديثةً وخفيفةً فوق طبقة جافاسكربت الأصيلة Native، كما يتمتع بتوافق كبير مع الإصدارات السابقة واللاحقة لمساعدة الشركات على مواكبة أحدث إصدارات Ember وأحدث الاتفاقيات التي يقودها المجتمع.
</p>

<p>
	تُعَدّ المكونات حزمًا من شيفرات السلوك والتنسيق Style والتوصيف Markup التي تشبه إلى حد كبير ما توفره أطر عمل الواجهة الأمامية الأخرى مثل <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%88%D8%A7%D8%B2%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D8%A3%D8%B7%D8%B1-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D9%85%D8%A7%D9%85%D9%8A%D8%A9-angular-%D9%88-react-%D9%88-vue-r1596/" rel="">React و Vue و Angular</a>، كما يوفر جانب الخدمة حالةً مشتركةً طويلة الأمد وسلوكًا وواجهةً للتكامل مع المكتبات أو الأنظمة الأخرى، إذ يُعَدّ الموجِّه Router الذي سنشرحه لاحقًا خدمةً مثلًا، وتشكّل المكونات والخدمات القسم الأكبر من تطبيق EmberJS.
</p>

<p>
	هذا المقال جزء من سلسلة تقديمية حول إطار العمل Ember وإليك فهرس كامل السلسلة:
</p>

<ul>
<li>
		مقدمة إلى إطار العمل Ember
	</li>
	<li>
		بنية تطبيق إطار العمل Ember وتقسيمها إلى مكونات
	</li>
	<li>
		تنفيذ التفاعل في تطبيق Ember: الأحداث والأصناف والحالة
	</li>
	<li>
		تنفيذ التفاعل في تطبيق Ember: وظيفة التذييل والعرض الشرطي
	</li>
	<li>
		التوجيه Routing في إطار العمل Ember
	</li>
</ul>
<h2>
	حالات الاستخدام
</h2>

<p>
	يناسب إطار عمل EmberJS بناء التطبيقات التي تستهدف إحدى السمتين التاليتين أو كلتيهما:
</p>

<ul>
<li>
		تطبيقات الصفحة الواحدة، بما في ذلك تطبيقات الويب الشبيهة بالتطبيقات الأصيلة و<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%85%D9%8A%D8%A9-pwa-r832/" rel="">تطبيقات الويب التقدمية</a> Progressive Web Apps أو PWA اختصارًا.

		<ul>
<li>
				يعمل إطار عمل Ember بصورة أفضل عندما يشكِّل الواجهة الأمامية الكاملة لتطبيقك.
			</li>
		</ul>
</li>
	<li>
		زيادة التماسك بين العديد من تقنيات الفرق البرمجية.
		<ul>
<li>
				تتيح أفضل الممارسات التي يدعمها المجتمع سرعة تطوير أكبر على المدى الطويل.
			</li>
			<li>
				يملك إطار عمل Ember اصطلاحات أو اتفاقيات واضحة ومفيدة لفرض التناسق ومساعدة أعضاء الفريق على العمل بسرعة.
			</li>
		</ul>
</li>
</ul>
<h3>
	إضافات إطار عمل Ember
</h3>

<p>
	يمتلك إطار العمل EmberJS معمارية الإضافات Plugin، مما يعني أنه يمكن تثبيت الإضافات Add-ons وتوفير وظائف إضافية دون كثير من الإعداد إذا وُجِد، ومن هذه الإضافات:
</p>

<ul>
<li>
		<a href="https://github.com/ef4/prember" rel="external nofollow">PREmber</a>: تصيير Rendering موقع ويب ساكن للمدونات أو المحتوى التسويقي.
	</li>
	<li>
		<a href="https://ember-fastboot.com/" rel="external nofollow">FastBoot</a>: تصيير من طرف الخادم، بما في ذلك <a href="https://academy.hsoub.com/marketing/search-engine-optimisation/%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D9%85%D8%AD%D8%B1%D9%83%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-seo-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%AA%D9%87%D8%A7-%D9%84%D9%85%D9%88%D9%82%D8%B9%D9%83-%D8%A7%D9%84%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A-r490/" rel="">تحسين محركات البحث SEO</a>، أو تحسين أداء التصيير الأولي لصفحات الويب المعقدة عالية التفاعل.
	</li>
	<li>
		<a href="https://empress-blog.netlify.app/welcome/" rel="external nofollow">empress-blog</a>: تأليف منشورات المدونات باستخدام لغة ماركداون <a href="https://academy.hsoub.com/apps/productivity/%D9%85%D8%A7%D8%B1%D9%83%D8%AF%D8%A7%D9%88%D9%86-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86-r289/" rel="">Markdown</a> مع تحسين محركات البحث SEO باستخدام الإضافة PREmber.
	</li>
	<li>
		<a href="https://ember-service-worker.com/" rel="external nofollow">ember-service-worker</a>: إعداد <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%85%D9%8A%D8%A9-pwa%D8%9F-r1480/" rel="">تطبيق ويب تقدمي PWA</a> بحيث يمكن تثبيت التطبيق على الأجهزة المحمولة مثل تثبيته من متجر تطبيقات الجهاز نفسه.
	</li>
</ul>
<h3>
	تطبيقات الجوال الأصيلة
</h3>

<p>
	يمكن استخدام إطار عمل Ember مع تطبيقات الأجهزة المحمولة الأصيلة مع جسر بين تطبيقات الهاتف المحمول الأصيلة <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-javascript-r664/" rel="">ولغة جافاسكربت</a> مثل الجسر الذي توفره واجهة <a href="http://corber.io/" rel="external nofollow">Corber</a>.
</p>

<h2>
	الآراء والأعراف
</h2>

<p>
	يُعَدّ إطار العمل EmberJS أحد أكثر أطر عمل الواجهة الأمامية تشبثًا برأيه، وتُعَدّ الآراء في Ember مجموعةً من الاتفاقيات أو الأعراف التي تساعد على زيادة كفاءة المطورين، ولكن يجب تعلّم تلك الاتفاقيات، إذ تساعد الآراء التي تدعم الاتفاقيات في تقليل الاختلافات بين التطبيقات مثل اللغات المُستخدَمة والنظام المجتمعي بفضل تعريف هذه الاتفاقيات ومشاركتها، وهو هدف مشترك بين جميع الأطر المتشبثة برأيها، ويصبح المطورون بعدها أكثر قدرةً على التبديل بين المشاريع والتطبيقات دون الحاجة إلى إعادة تعلم المعمارية والأنماط والمصطلحات وما إلى ذلك، وستلاحظ خلال هذا المقال آراء إطار العمل Ember مثل اصطلاحات تسمية ملفات المكونات.
</p>

<h2>
	ارتباط إطار عمل Ember مع لغة جافاسكربت الصرفة Vanilla JavaScript
</h2>

<p>
	بُني إطار عمل Ember على تقنيات جافاسكربت، ويُعَدّ طبقةً رقيقةً فوق <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-r1375/" rel="">البرمجة التقليدية كائنية التوجه</a>، مع السماح للمطورين باستخدام تقنيات <a href="https://academy.hsoub.com/programming/general/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%88%D8%B8%D9%8A%D9%81%D9%8A%D8%A9-functional-programming-r1391/" rel="">البرمجة الوظيفية</a>.
</p>

<p>
	يستخدِم Ember صيغتين رئيسيتين هما:
</p>

<ul>
<li>
		جافاسكربت أو لغة <a href="https://wiki.hsoub.com/TypeScript" rel="external">TypeScript</a> اختياريًا.
	</li>
	<li>
		لغة قوالب Ember الخاصة التي تعتمد على لغة <a href="https://handlebarsjs.com/guide/" rel="external nofollow">Handlebars</a>.
	</li>
</ul>
<p>
	تُستخدَم لغة القوالب Templating Language لتحسين عملية البناء ووقت التشغيل، وهي مجموعة شاملة من لغة HTML، وبالتالي يمكن لأيّ شخص يعرف لغة HTML تقديم مساهمات مهمة لأيّ مشروع من مشاريع Ember، كما يمكن للمصممين وغيرهم من غير المطورين المساهمة في قوالب الصفحات دون أي معرفة بلغة جافاسكربت، ثم يمكن إضافة التفاعل لاحقًا، كما تتيح لغة القوالب هذه حمولات أصول أخف نظرًا لتصريف القوالب في شيفرة ثنائية Byte Code يمكن تحليلها بسرعة أكبر من تحليل شيفرة جافاسكربت.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: تتيح آلة Glimmer الافتراضية تتبّع تغيير <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">نموذج DOM</a> بسرعة دون الحاجة إلى إدارة واستخدام الفرق للتمثيل الافتراضي المُخزَّن مؤقتًا، وهو نهج شائع للتخفيف من بطء تغييرات الدخل والخرج في نموذج DOM.
		</p>
	</div>
</blockquote>

<p>
	كل شيء آخر في Ember هو جافاسكربت وخاصةً أصناف جافاسكربت التي تُعَدّ المكان الذي تعمل فيه معظم أجزاء إطار العمل حيث توجد الأصناف الأبناء، ويكون لكل نوع من الأشياء غرض مختلف وموقع متوقع مختلف ضمن مشروعك.
</p>

<p>
	يوضِّح الشكل التالي تأثير Ember على جافاسكربت في المشاريع النموذجية، إذ يوضِّح كيف أن أقل من 20% من شيفرة JS المكتوبة خاصةً بإطار العمل Ember.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="104462" href="https://academy.hsoub.com/uploads/monthly_2022_08/01_20percent-js-specific-ember.png.6d76c41a252dc1a3a9c1f7a8caba2654.png" rel=""><img alt="01_20percent-js-specific-ember.png" class="ipsImage ipsImage_thumbnailed" data-fileid="104462" data-unique="mwttt9947" src="https://academy.hsoub.com/uploads/monthly_2022_08/01_20percent-js-specific-ember.png.6d76c41a252dc1a3a9c1f7a8caba2654.png" style="width: 700px; height: auto;"></a>
</p>

<h2>
	بدء استخدام Ember
</h2>

<p>
	سننشئ أولًا نسخةً من نموذج تطبيق <a href="https://todomvc.com/" rel="external nofollow">TodoMVC</a> التقليدي لتعلُّم كيفية استخدام أساسيات إطار عمل Ember، إذ يُعَدّ تطبيق TodoMVC تطبيقًا أساسيًا لتتبّع المهام، ويُستخدَم في العديد من التقنيات المختلفة، وإليك <a href="https://nullvoxpopuli.github.io/ember-todomvc-tutorial/" rel="external nofollow">نسخةً مكتملةً</a> منه ليكون مرجع لك.
</p>

<h3>
	إنشاء تطبيق جديد في Ember
</h3>

<p>
	يحتوي مشروع TodoMVC على بعض المشاكل من حيث الالتزام بممارسات الويب التي يمكن الوصول إليها افتراضيًا، وهناك نوعان من مشاكل جيت هاب GitHub المفتوحة حول هذا الموضوع في مجموعة مشاريع TodoMVC هي:
</p>

<ul>
<li>
		<a href="https://github.com/tastejs/todomvc/issues/1017" rel="external nofollow">إضافة وصول مستخدِمي لوحة المفاتيح إلى العروض التوضيحية</a>.
	</li>
	<li>
		<a href="https://github.com/tastejs/todomvc-app-css/issues/35" rel="external nofollow">إعادة تفعيل المخطط حول العناصر القابلة للتركيز</a>.
	</li>
</ul>
<p>
	يهتم إطار Ember بموضوع إمكانية الوصول accessibility كثيرًا وأن تكون التطبيقات المبنية فيه سهلة الوصول لكامل المستخدمين حتى بمن فيهم أي إعاقة وقد وفر <a href="https://guides.emberjs.com/release/accessibility/" rel="external nofollow">دليلًا شاملًا عنه</a>، ولكن بما أنّ هذا المقال يركِّز على جانب جافاسكربت في إنشاء تطبيق ويب صغير، فإنّ قيمة TodoMVC تأتي من توفير ملفات CSS المبنيّة مسبقًا وبنية HTML الموصَى بها، مما يلغي الاختلافات بين التطبيقات، كما يسمح بإجراء موازنة أسهل بينها، وسنركِّز لاحقًا على إضافة شيفرة إلى تطبيقنا لإصلاح بعض أكبر أخطاء تطبيق TodoMVC.
</p>

<h2>
	تثبيت أدوات Ember
</h2>

<p>
	يستخدِم Ember واجهة سطر الأوامر CLI لبناء أجزاء من تطبيقك وإنشاء شيفرتها المساعدة Scaffolding.
</p>

<p>
	أولًا، ستحتاج إلى تثبيت أداة node و<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-%D9%81%D9%8A-nodejs-r1465/" rel="">مدير الحزم npm</a> قبل تمكّنك من تثبيت أداة ember-cli، كما يمكنك الانتقال إلى مقال <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">دليل استخدام سطر الأوامر في عملية تطوير الويب من طرف العميل</a> لمعرفة كيفية تثبيت node و npm إذا لم تكن مثبّتةً لديك مسبقًا. ثانيًا، اكتب الأمر التالي في طرفيتك لتثبيت أداة <code>ember-cli</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5348_14" style="">
<span class="pln">npm install </span><span class="pun">-</span><span class="pln">g ember</span><span class="pun">-</span><span class="pln">cli</span></pre>

<p>
	توفِّر هذه الأداة برنامج <code>ember</code> في طرفيتك، وتُستخدَم لإنشاء وبناء وتطوير واختبار تطبيقك وإنشاء شيفرته المساعِدة، كما يمكنك تشغيل الأمر <code>ember --help</code> للحصول على قائمة كاملة بالأوامر وخياراتها.
</p>

<p>
	ثالثًا، يمكنك إنشاء تطبيق جديد تمامًا من خلال كتابة الأمر التالي في طرفيتك، مما يؤدي إلى إنشاء مجلد جديد له الاسم todomvc ضمن المجلد الذي أنت فيه حاليًا، بحيث يحتوي على أدوات لبناء تطبيق Ember جديد، وتأكد من الانتقال إلى مكان مناسب في الطرفية مثل مجلد "سطح المكتب" أو مجلد "المستندات" لتعثر على مجلد تطبيقك بسهولة قبل تشغيل الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5348_16" style="">
<span class="pln">ember </span><span class="kwd">new</span><span class="pln"> todomvc</span></pre>

<p>
	أو شغّل الأمر التالي على نظام ويندوز:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5348_18" style="">
<span class="pln">npx ember</span><span class="pun">-</span><span class="pln">cli </span><span class="kwd">new</span><span class="pln"> todomvc</span></pre>

<p>
	يؤدي هذا الأمر إلى إنشاء بيئة تطوير تطبيقات جاهزة للإنتاج تتضمن افتراضيًا الميزات التالية:
</p>

<ul>
<li>
		خادم التطوير مع إعادة التحميل المباشر.
	</li>
	<li>
		معمارية الإضافات التي تسمح لحزم الطرف الثالث بتحسين تطبيقك.
	</li>
	<li>
		أحدث إصدار من جافاسكربت متكامل مع Babel و Webpack.
	</li>
	<li>
		بيئة اختبار آلية تدير اختباراتك في المتصفح، مما يتيح لك الاختبار مثل المستخدِم العادي.
	</li>
	<li>
		عملية التحويل Transpilation والتصغير Minification لكل من شيفرات CSS وجافاسكربت الخاصة بعمليات البناء للإنتاج.
	</li>
	<li>
		اتباع العرف السائد في كتابة الشيفرة مما يقلل الاختلافات بين التطبيقات ويسمح بتبديل السياق بسهولة.
	</li>
</ul>
<h2>
	الاستعداد لبناء مشروع Ember
</h2>

<p>
	ستحتاج إلى محرِّر شيفرات قبل الاستمرار بمشروعك الجديد، فإذا لم يكن لديك محرّر مُعَدّ مسبقًا، فإنّ <a href="https://www.notion.so/Editors-Tooling-5da96f0b2baf4ce1bf3fd58e3b60c7f6" rel="external nofollow">Ember Atlas</a> لديه بعض الإرشادات حول كيفية إعداد المحرّرات المختلفة.
</p>

<h3>
	تثبيت الأصول المشتركة لمشاريع TodoMVC
</h3>

<p>
	لا يُعَدّ تثبيت الأصول أو ملفات المشروع المشتركة خطوةً مطلوبةً للمشاريع الجديدة، لكنه يسمح باستخدام ملفات CSS المشتركة الحالية حتى لا نخمّن ما هو ملف CSS المطلوب لإنشاء أنماط أو تنسيقات مشروع TodoMVC.
</p>

<p>
	أولًا، انتقل أولًا إلى المجلد todomvc في الطرفية باستخدام الأمر <code>cd todomvc</code> في نظامَي macOS أو لينكس Linux مثلًا.
</p>

<p>
	ثانيًا، شغّل الأمر التالي لوضع ملف CSS المشترك الخاص بمشروع todomvc ضمن تطبيقك:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5348_20" style="">
<span class="pln">npm install </span><span class="pun">--</span><span class="pln">save</span><span class="pun">-</span><span class="pln">dev todomvc</span><span class="pun">-</span><span class="pln">app</span><span class="pun">-</span><span class="pln">css todomvc</span><span class="pun">-</span><span class="pln">common</span></pre>

<p>
	ثالثًا، ابحث بعد ذلك عن الملف <a href="https://github.com/ember-cli/ember-cli/blob/master/blueprints/app/files/ember-cli-build.js" rel="external nofollow">ember-cli-build.js</a> في المجلد todomvc الموجود في المجلد الجذر، وافتحه في محرر الشيفرة الذي اخترته، إذ يُعَدّ الملف ember-cli-build.js مسؤولًا عن إعداد التفاصيل حول كيفية بناء مشروعك بما في ذلك تجميع كل ملفاتك مع بعضها البعض وتصغير الأصول وإنشاء خرائط الشيفرة البرمجية، لذلك ليس هناك داع للقلق بشأن هذا الملف، كما سنضيف سطورًا إلى الملف ember-cli-build.js لاستيراد ملفات CSS المشتركة، بحيث تصبح جزءًا من عملية البناء دون الحاجة إلى استيرادها <code>‎@import</code> صراحةً في الملف app.css، إذ سيتطلب ذلك إعادة كتابة <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87-r1435/" rel="">عنوان URL</a> في وقت البناء وبالتالي سيكون أقل كفاءة وأكثر تعقيدًا في الإعداد. رابعًا، ابحث عن الشيفرة التالية في الملف ember-cli-build.js:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5348_23" style="">
<span class="pln">let app </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">EmberApp</span><span class="pun">(</span><span class="pln">defaults</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// أضف خيارات هنا</span><span class="pln">
  </span><span class="pun">});</span></pre>

<p>
	خامسًا، أضف الأسطر التالية بعد ذلك قبل حفظ الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5348_25" style="">
<span class="pln">  app</span><span class="pun">.</span><span class="kwd">import</span><span class="pun">(</span><span class="str">'node_modules/todomvc-common/base.css'</span><span class="pun">);</span><span class="pln">
  app</span><span class="pun">.</span><span class="kwd">import</span><span class="pun">(</span><span class="str">'node_modules/todomvc-app-css/index.css'</span><span class="pun">);</span></pre>

<p>
	سادسًا، أخيرًا، ابحث عن الملف app.css الموجود في المسار app/styles/app.css، والصق ما يلي فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5348_27" style="">
<span class="pun">:</span><span class="pln">focus</span><span class="pun">,</span><span class="pln">
</span><span class="pun">.</span><span class="pln">view label</span><span class="pun">:</span><span class="pln">focus</span><span class="pun">,</span><span class="pln">
</span><span class="pun">.</span><span class="pln">todo</span><span class="pun">-</span><span class="pln">list li </span><span class="pun">.</span><span class="pln">toggle</span><span class="pun">:</span><span class="pln">focus </span><span class="pun">+</span><span class="pln"> label</span><span class="pun">,</span><span class="pln">
</span><span class="pun">.</span><span class="pln">toggle</span><span class="pun">-</span><span class="pln">all</span><span class="pun">:</span><span class="pln">focus </span><span class="pun">+</span><span class="pln"> label </span><span class="pun">{</span><span class="pln">
  outline</span><span class="pun">:</span><span class="pln"> </span><span class="pun">#</span><span class="pln">d86f95 solid </span><span class="pun">!</span><span class="pln">important</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: تُطلَب كتابة‫ ‎!important لأن أنماط todomvc تعطل المخطط outline عمدًا.
		</p>
	</div>
</blockquote>

<p>
	يعدِّل ملف CSS بعض الأنماط التي توفرها حزمة npm والتي هي <code>todomvc-app-css</code>، مما يسمح بظهور تركيز لوحة المفاتيح ويؤدي إلى حد ما إلى إصلاح أحد عيوب الشمولية الرئيسية لتطبيق TodoMVC الافتراضي.
</p>

<h3>
	بدء تشغيل خادم التطوير
</h3>

<p>
	يمكنك بدء تشغيل التطبيق في وضع التطوير عن طريق كتابة الأمر التالي في الطرفية أثناء وجودك ضمن المجلد todomvc:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5348_29" style="">
<span class="pln">ember server</span></pre>

<p>
	ويجب أن يظهر لديك خرج مشابه لما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5348_31" style="">
<span class="typ">Build</span><span class="pln"> successful </span><span class="pun">(</span><span class="lit">190ms</span><span class="pun">)</span><span class="pln"> </span><span class="pun">–</span><span class="pln"> </span><span class="typ">Serving</span><span class="pln"> on http</span><span class="pun">:</span><span class="com">//localhost:4200/</span><span class="pln">

</span><span class="typ">Slowest</span><span class="pln"> </span><span class="typ">Nodes</span><span class="pln"> </span><span class="pun">(</span><span class="pln">totalTime </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">%)</span><span class="pln">          </span><span class="pun">|</span><span class="pln"> </span><span class="typ">Total</span><span class="pln"> </span><span class="pun">(</span><span class="pln">avg</span><span class="pun">)</span><span class="pln">
</span><span class="pun">-----------------------------------------+-----------</span><span class="pln">
</span><span class="typ">BroccoliMergeTrees</span><span class="pln"> </span><span class="pun">(</span><span class="lit">17</span><span class="pun">)</span><span class="pln">                  </span><span class="pun">|</span><span class="pln"> </span><span class="lit">35ms</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pln"> ms</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Package</span><span class="pln"> </span><span class="pun">/</span><span class="pln">assets</span><span class="pun">/</span><span class="pln">vendor</span><span class="pun">.</span><span class="pln">js </span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span><span class="pln">            </span><span class="pun">|</span><span class="pln"> </span><span class="lit">13ms</span><span class="pln">
</span><span class="typ">Concat</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Vendor</span><span class="pln"> </span><span class="typ">Styles</span><span class="pun">/</span><span class="pln">assets</span><span class="pun">/</span><span class="pln">vend</span><span class="pun">...</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="lit">12ms</span></pre>

<p>
	يُشغَّل خادم التطوير على المضيف المحلي <code><a href="http://localhost:4200" ipsnoembed="false" rel="external nofollow">http://localhost:4200</a></code>، والذي يمكنك زيارته في متصفحك للتحقق من عملك حتى الآن.
</p>

<p>
	إذا كان كل شيء على ما يرام، فسترى صفحةً تشبه الصفحة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="104463" href="https://academy.hsoub.com/uploads/monthly_2022_08/02_ember-start-page.png.3148db3a6e45b2826ab8f4634114486a.png" rel=""><img alt="02_ember-start-page.png" class="ipsImage ipsImage_thumbnailed" data-fileid="104463" data-unique="z5w0v02r7" src="https://academy.hsoub.com/uploads/monthly_2022_08/02_ember-start-page.png.3148db3a6e45b2826ab8f4634114486a.png" style="width: 500px; height: auto;"></a>
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: ستواجه في أنظمة ويندوز التي لا تحتوي على نظام ويندوز الفرعي للينكس Windows Subsystem for Linux -أو WSL اختصارًا- أوقات إنشاء أبطأ بالموازنة مع أنظمة ماك macOS و<a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">لينكس Linux</a> وويندوز التي تحتوي على نظام WSL.
		</p>
	</div>
</blockquote>

<h2>
	الخلاصة
</h2>

<p>
	وصلنا إلى النقطة التي يمكننا فيها البدء في بناء نموذجنا لتطبيق TodoMVC في إطار عمل Ember، وسنتعرّف في المقال التالي على بناء بنية شيفرة توصيف تطبيقنا بوصفها مجموعة من المكونات المنطقية.
</p>

<p>
	ترجمة -وبتصرّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_getting_started" rel="external nofollow">Getting started with Ember</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%88%D8%A7%D8%B2%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D8%A3%D8%B7%D8%B1-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D9%85%D8%A7%D9%85%D9%8A%D8%A9-angular-%D9%88-react-%D9%88-vue-r1596/" rel="">مقارنة بين أطر الواجهات الأمامية: Angular و React و Vue</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1648</guid><pubDate>Sat, 06 Aug 2022 16:01:02 +0000</pubDate></item><item><title>&#x645;&#x627;&#x630;&#x627; &#x628;&#x639;&#x62F; &#x62A;&#x639;&#x644;&#x645; Node.js &#x648; React.js</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D8%A7%D8%B0%D8%A7-%D8%A8%D8%B9%D8%AF-%D8%AA%D8%B9%D9%84%D9%85-nodejs-%D9%88-reactjs-r1832/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_12/639e99620d982_---REACT-NODE.png.b294ea757e36ce48db3152c29bb86fad.png" /></p>

<p>
	يسأل العديد من الذين قد تعلمو أساسيات لغة <a href="https://academy.hsoub.com/programming/javascript/nodejs/" rel="">Node.js</a> و <a href="https://academy.hsoub.com/programming/javascript/react/" rel="">React.js</a> عن كيفية الاستفادة من هذه الخبرات وما هي المجالات التي يمكنهم توظيف خبراتهم في بيئة Node.js و React.js بها.
</p>

<p>
	سنطرح لك في الفيديو عددًا من الأفكار والمشاريع والتي ستشكل اختبارًا حقيقي لك وستساعدك على تطوير خبرتك في لغة Node.js وقواعد بيانات React.js
</p>

<p style="text-align: center;">
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="409" title="ماذا بعد تعلم Node.js و React.js" width="727" src="https://www.youtube.com/embed/BXN5VjA6M8o"></iframe>
</p>

<p>
	إذا كنت مبتدئًا في هذه اللغات أو كنت قد اكتفيت إلى الآن بتعلم أساسياتها وحدها، وكنت مهتمًا بتعلمها باحترافية، فيُنصح بالانضمام إلى <a href="https://academy.hsoub.com/learn/javascript-application-development/" rel="">دورة تطوير التطبيقات باستخدام لغة JavaScript</a> المقدمة من أكاديمية حسوب، للتعرف أكثر على اللغتين وتعلم التطوير الاحترافي عبرهما، ولا تنسَ خلال رحلة تعلمك وعملك الاستعانة بالتوثيقات المجانية المتاحة على <a href="https://wiki.hsoub.com/" rel="external">موسوعة حسوب</a>.
</p>
]]></description><guid isPermaLink="false">1832</guid><pubDate>Sun, 24 Jul 2022 15:00:00 +0000</pubDate></item><item><title>&#x645;&#x642;&#x627;&#x631;&#x646;&#x629; &#x628;&#x64A;&#x646; &#x623;&#x637;&#x631; &#x627;&#x644;&#x648;&#x627;&#x62C;&#x647;&#x627;&#x62A; &#x627;&#x644;&#x623;&#x645;&#x627;&#x645;&#x64A;&#x629;: Angular &#x648; React &#x648; Vue</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D8%A3%D8%B7%D8%B1-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D9%85%D8%A7%D9%85%D9%8A%D8%A9-angular-%D9%88-react-%D9%88-vue-r1596/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/62973719057ad_--Angular-React-Vue.jpg.ee70a6887ad739b89a9e1fc68ffef0b5.jpg" /></p>
<p>
	تطورت <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AA%D9%89-%D9%86%D8%B3%D8%AA%D8%B9%D9%85%D9%84-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-%D9%84%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-javascript-r477/" rel="">أطر عمل جافاسكربت</a> للواجهات الأمامية في السنوات الأخيرة بوتيرة سريعة للغاية، الأمر الذي جعل الكثير من المبتدئين يتيهون في اختيار ما هو الأنسب لهم، ولم يقتصر الأمر على المبرمجين فحسب وإنما يعاني بعض رواد الأعمال أيضًا في اختيار الإطار المناسب لمشروعهم، فإذا كنت من هؤلاء الأشخاص ولم تستطع تحديد <a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">إطار عمل</a> جافاسكربت المراد استخدامه للواجهات الأمامية، فسيساعدك هذا المقال في اتخاذ قرار المناسب.
</p>

<p>
	سنغطي في هذا المقال جوانبًا مختلفة من أطر عمل <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="">Angular</a> و <a href="https://academy.hsoub.com/programming/javascript/vuejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-vuejs-r989/" rel="">Vue</a> و <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%A7-%D9%87%D9%8A-react%D8%9F-r773/" rel="">React</a> لنرى كيف تناسب احتياجاتك. هذا المقال ليس مجرد مقارنة بين هذه الأطر وإنما سنحاول جعله هيكلًا معياريًا للمساعدة في الحكم على أطر عمل جافاسكربت في الواجهة الأمامية بشكل عام. في حالة إصدار إطار عمل جديد في العام المقبل، ستعرف بالضبط ما هي الأمور التي يجب عليك النظر إليها في الإطار الجديد!
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix" data-gramm="false">
		<p>
			<strong>ملاحظات:</strong>
		</p>

		<ul>
			<li>
				نسعى لتجنب التحيز بين أطر العمل ولكن بالتأكيد هناك بعض المشاكل التي تحلها بعض أطر بطريقة أفضل من الأخرى وهذا أمر طبيعي.
			</li>
			<li>
				نفترض في هذا المقال بأن لديك معرفة أساسية <a href="https://wiki.hsoub.com/JavaScript" rel="external">بلغة جافاسكربت.</a>
			</li>
		</ul>
	</div>
</blockquote>

<h2>
	تاريخ ونبذة موجزة لأطر العمل Angular vs React vs Vue
</h2>

<p>
	قبل أن ندخل في التفاصيل التقنية لنتحدث أولًا عن التاريخ الموجز وراء هذه الأطر فقط لفهم مدى نضوجها وتطورها بمرور الوقت. في الجدول أدناه نلاحظ المعلومات الأساسية لكل إطار.
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
	<thead>
		<tr>
			<th>
				وجه المقارنة
			</th>
			<th>
				إطار العمل Angular
			</th>
			<th>
				إطار العمل React
			</th>
			<th>
				إطار العمل Vue.js
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				تاريخ صدوره
			</td>
			<td>
				2010
			</td>
			<td>
				2013
			</td>
			<td>
				2014
			</td>
		</tr>
		<tr>
			<td>
				الموقع الرسمي
			</td>
			<td>
				<a href="angular.io" rel="">angular.io</a>
			</td>
			<td>
				<a href="reactjs.org" rel="">reactjs.org</a>
			</td>
			<td>
				<a href="vuejs.org" rel="">vuejs.org</a>
			</td>
		</tr>
		<tr>
			<td>
				الإصدار الحالي
			</td>
			<td>
				‎13.x
			</td>
			<td>
				‎17.x
			</td>
			<td>
				‎3.x
			</td>
		</tr>
		<tr>
			<td>
				فريق التطوير
			</td>
			<td>
				شركة غوغل وجمهور المطورين
			</td>
			<td>
				شركة فيسبوك وجمهور المطورين
			</td>
			<td>
				جمهور المطورين فقط
			</td>
		</tr>
	</tbody>
</table>

<h3>
	تاريخ موجز عن إطار العمل Angular
</h3>

<p>
	صدر إطار العمل Angular (ويعرف سابقًا <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-angularjs-r176/" rel="">AngularJS</a>) لأول مرة في عام 2010 والذي طورته شركة غوغل مما يجعله الأقدم بين الإطارات، وهو إطار عمل جافاسكربت مفتوح المصدر قائم على TypeScript.
</p>

<p>
	صُممُ هذا الإطار لمساعدة المطورين على حل المشكلات عند إنشائهم لتطبيقات الصفحة الواحدة SPA وليتمكّنوا من بناء صفحات ويب ديناميكيّة باستخدامهم لنصوص تصريحية بسيطة. يتبع هذا الإطار النمط الهيكلي MVC (وهي اختصار Model-View-Controller أي نموذج-طريقة عرض-مُتحكِّم) وبنية MVVM (وهي اختصار Model-View-View-Model أي نموذج-طريقة عرض-طريقة عرض-مُتحكِّم) وغيرها من الأنماط الهيكلية المختلفة، مما يجعلها قادرة على رفع مستوى الأمان والمرونة وسهولة التوسع في صفحات الويب، وكذلك ترتيب عملية تبادل المعلومات. حدث تحول كبير في عام 2016 عندما أصدرت غوغل إصدار Angular 2 إذ أسقطت "JS" من الاسم الأصلي AngularJS وأصبح Angular فقط، وبالرغم من ذلك لا يزال AngularJS (الإصدار 1) يتلقى تحديثات، سنركز المناقشة على أحدث إصدار مستقر وهو Angular 13، والذي صدر في نوفمبر 2021.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix" data-gramm="false">
		<p>
			<strong>توضيح</strong>: تطبيقات الصفحة الواحدة SPA (وهي اختصار Single Page Applications) هي تطبيقات ويب لا تتطلب إعادة تحميل الصفحة عند استخدامها. تتضمن أمثلة هذه التطبيقات خرائط غوغل وتطبيق فيسبوك وتطبيق البريد الإلكتروني Gmail، بالإضافة إلى ذلك تتميز تطبيقات الصفحة الواحدة بتجربة مستخدم متميزة نظرًا لتقليد البيئة التطبيقات الطبيعية، بمعنى آخر كما يوحي الاسم إنها صفحة ويب واحدة تتحكم في عرض وتنزيل وترتيب محتوى التطبيق من خلال جافاسكربت.
		</p>
	</div>
</blockquote>

<p>
	تعد بنية Model–View–Controller بنيةً تفصل بين صفحة الويب التي تعرض المعلومات View ومخزن المعلومات Model باستخدام العقل المدبر أو المتحكم Controller، الأمر الذي من يسهل كتابة الشيفرة البرمجية وصيانتها وتعديلها ويزيد من نسبة الأمان.
</p>

<h3>
	تاريخ موجز عن إطار العمل React
</h3>

<p>
	إطار العمل <a href="https://wiki.hsoub.com/React" rel="external">React</a> (والمعروفة أيضًا باسم <a href="https://academy.hsoub.com/programming/javascript/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-reactjs-%d9%85%d9%83%d8%aa%d8%a8%d8%a9-%d8%aa%d8%b7%d9%88%d9%8a%d8%b1-%d8%a7%d9%84%d9%88%d8%a7%d8%ac%d9%87%d8%a7%d8%aa-%d8%a7%d9%84%d8%b1%d8%b3%d9%88%d9%85%d9%8a%d8%a9-%d9%85%d9%86-%d9%81%d9%8a%d8%b3-%d8%a8%d9%88%d9%83-r112/" rel="">ReactJS</a>) وهي مكتبة جافاسكربت مفتوحة المصدر تُستخدَم لبناء واجهات المستخدم طورتها شركة فيسبوك (ميتا حاليًا) وصدرت في عام 2013، وتستخدم الشركة إطار العمل React في العديد من منتجاتها (مثل منصة فيسبوك وانستغرام وواتساب).
</p>

<p>
	صُمّم إطار العمل React بصورة أساسية لمساعدة المطورين على حل المشكلات المتعلقة بتوفير البيانات الكثيرة بطريقة فعالة لقواعد البيانات الكبيرة، ويعتمد إطار عمل React على مفهوم المكوّنات Components، إذ يجب عليك في البداية بناء مكوّنات مُغلَّفة تُدير حالتها الخاصّة، ومن ثمّ تُركِّب هذه المكوّنات مع بعضها لإنشاء واجهات مستخدم.
</p>

<h3>
	تاريخ موجز عن إطار العمل Vue.js
</h3>

<p>
	إطار عمل Vue.js (والمعروف أيضًا باسم Vue أو VueJS)، ويعد هذا الإطار الأصغر سنًا بين أعضاء مجموعة المقارنة. طور هذا الإطار إيفان يو Evan You (الموظف السابق في شركة غوغل) في عام 2014. بعد عمل إيفان مع إطار العمل Angular قرر إنشاء مكتبة <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">جافاسكربت</a> مماثلة باستخدام مزايا Angular وجعل إطار العمل الجديد أكثر خفة وسهولة، ولهذا السبب يوجد الكثير من القواسم المشتركة بين الإطارين.
</p>

<p>
	شهد إطار العمل Vue على مدار السنوات الماضية تحولًا كبيرًا في شعبيته، وبالرغم من أنه لا يحظى بدعم من شركة كبيرة مثل سابقيه، إلا أنه استطاع الحصول على حصة جيدة في سوق أُطر عمل جافاسكربت. يعتمد تطوير هذا الإطار كليًا على مشاركات جمهور المطورين.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix" data-gramm="false">
		<p>
			<strong>ملاحظة</strong>: قبل استخدام مشروع أو إطار عمل مفتوح المصدر تأكد من مراجعة نوعية ترخيصه. جميع الأطر السابقة تستخدم ترخيص MIT، والذي يوفر قيودًا محدودة على إعادة الاستخدام في البرامج الاحتكارية. ولكن عمومًا يجب الانتباه إلى هذه النقطة لتجنب الآثار المترتبة على استخدام الخاطئ لأي إطار عمل أو مشروع.
		</p>
	</div>
</blockquote>

<h2>
	نظرة أعمق لمميزات ومساوئ كل إطار
</h2>

<p>
	سنغوص الآن أكثر ونفهم أهم المزايا والعيوب الرئيسية لهذه الأطر والفروقات بينها وذلك لمساعدة المتخصصين أو المهندسين التقنيين في اختيار الأفضل لاحتياجات التطوير الخاصة بهم.
</p>

<h3>
	سوق العمل
</h3>

<p>
	كمبرمج مبتدئ يجب أن نهتم في البداية إلى اتجاهات السوق ومتطلباته وكما <a href="https://zerotomastery.io/blog/tech-trends-showdown-react-vs-angular-vs-vue/" rel="external nofollow">يتضح</a> من اتجاهات أواخر عام 2018، فإن عدد الوظائف التي تتطلب مجموعة مهارات استخدام إطار Angular أو React هو نفسه تقريبًا، في حين أن Vue لا يزال جزءًا بسيطًا من هذه الحصة (حوالي 20٪ من نسبة عدد فرص العمل في بقية الأطر)، وعمومًا هذه الإحصائية قديمة وعامة وإذا رغبت في إحصائية أكثر حداثة فيمكنك تجربة البحث عبر Google Trends، والذي يستعرض اتجاهات البحث على مدار مدة زمنية معينة لوظائف React، ووظائف Angular، ووظائف Vue. تعمل مؤشرات غوغل أيضًا على تقسيمها حسب الموقع الجغرافي في حال رغبت في البحث في سوق العمل في بلدك.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="100372" href="https://academy.hsoub.com/uploads/monthly_2022_06/jobs.png.dd87c760214eaf15ac7a096c618ac36b.png" rel="" data-fileext="png"><img alt="jobs.png" class="ipsImage ipsImage_thumbnailed" data-fileid="100372" data-unique="3qtges3p4" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_06/jobs.thumb.png.1d291e1ca0d08c97689af5cbe8af97ae.png"></a>
</p>

<p>
	ومن الناحية النموذجية فإن أفضل رهان للمبتدئ من وجهة نظر سوق العمل الحالي هو أن يتعلم Angular أو React، نظرًا لأن شعبية إطار العمل Vue.js ما تزال محدودة فإن استخدامه محدودًا في الشركات الكبيرة، ويمكن أن يستغرق الأمر بعض الوقت حتى تتبنى هذه الشركات إطار Vue.js في مشاريعها وهذا سيتطلب من الإطار الوصول إلى مستوى أعلى من النضج والذي بدوره يتطلب عددًا أكبر من المطورين يعملون لتحسين إطار العمل Vue.js.
</p>

<h3>
	المجتمع والتطوير
</h3>

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

<p>
	دعونا نلقي نظرة على مجتمعات Angular vs React vs Vue فيما يتعلق بالإحصاءات الموجودة في مستودعات GitHub الخاصة بهم ونؤكد أن هذه الأرقام حتى تاريخ كتابة المقال (ولاحظ أن أرقام Vue تشمل أيضًا مستودع Vue 3.0 المنفصل):
</p>

<table>
	<thead>
		<tr>
			<th>
				وجه المقارنة
			</th>
			<th>
				إطار العمل Angular
			</th>
			<th>
				إطار العمل React
			</th>
			<th>
				إطار العمل Vue.js
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				عدد المراقبين
			</td>
			<td>
				3.1 ألف شخص
			</td>
			<td>
				6.7 ألف شخص
			</td>
			<td>
				6.2 ألف شخص
			</td>
		</tr>
		<tr>
			<td>
				عدد النجوم
			</td>
			<td>
				79 ألف نجمة
			</td>
			<td>
				181 ألف نجمة
			</td>
			<td>
				192 ألف نجمة
			</td>
		</tr>
		<tr>
			<td>
				عدد التفريعات
			</td>
			<td>
				20.7 ألف تفريعة
			</td>
			<td>
				36.8 ألف تفريعة
			</td>
			<td>
				31.2 ألف تفريعة
			</td>
		</tr>
		<tr>
			<td>
				عدد المساهمين
			</td>
			<td>
				1517 مساهم
			</td>
			<td>
				1535 مساهم
			</td>
			<td>
				404 مساهم
			</td>
		</tr>
	</tbody>
</table>

<p>
	عند المقارنة بين الأطر نلاحظ أن إطار العمل Vue لديه عددًا كبيرًا من المراقبين والنجوم والتفريعات، وهذا يعود لشعبيته بين المستخدمين. ومع ذلك، فإن عدد المساهمين في Vue أقل من Angular و React. أحد التفسيرات المحتملة هو أن Vue مطوّرة بالكامل من قبل مجتمع المصادر المفتوحة، في حين أن كل من إطار العمل Angular و React يطورهما عدد من موظفي غوغل وفيسبوك.
</p>

<p>
	من الإحصائيات المهمة أيضًا هي استطلاع الرأي السنوي الذي يجريه موقع Stackoverflow والذي استجاب إليه 67.593 ألف مبرمج جاء فيه تفوقًا واضحًا في استخدام إطار عمل React بالمقارنة مع بقية الأطر.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="101432" href="https://academy.hsoub.com/uploads/monthly_2022_06/insights.stackoverflow_com.png.7734f9befbbc92aa4d402105523c8bb2.png" rel="" data-fileext="png"><img alt="insights.stackoverflow.com.png" class="ipsImage ipsImage_thumbnailed" data-fileid="101432" data-unique="lho2c9i16" style="width: 450px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_06/insights.stackoverflow_com.thumb.png.81c0d8031c13a529ce439c5825d5f699.png"></a>
</p>

<p>
	المقياس الإضافي الذي يجب أن نلقي نظرة عليه وهو عدد مرات الاستخدام للإطار في موقع GitHub، والتي يجب تمكينها بواسطة مؤلف المستودع. يوضح هذا عدد المستودعات الأخرى الموجودة على GitHub والتي تعتمد على هذا المستودع. يُظهر إطار العمل Angular بأن عدد مستخدميه 2.1 مليون مستخدم، بينما يُظهر إطار العمل React حاليًا ما يقرب من 8.7 مليون مستخدم، بينما لا يُظهر إطار العمل Vue عدد مستخدميه.
</p>

<h3>
	تحديث الشيفرات البرمجية
</h3>

<p>
	نظرًا لأن بعض المشاريع كبيرة وتحتاج لدراسة كيفية تحديث على الشيفرة البرمجية وجعلها مواكبة لإطار العمل وأفضل الممارسات الخاصة به فيجب أن ننظر إلى كيفية تحديث الشيفرة لكل إطار. في الحقيقة غالبًا ما ستواجه العديد من المشكلات عند تحديث الشيفرة من إصدار إلى آخر، لذلك من المهم أن تبقى مطلعًا بصورة دائمة على تحديثات أُطر العمل لأن بعض التحديثات تكون مهمة وتتطلب تعديلات تتراوح بين بسيطة إلى كبيرة وهذا يؤثر على سير العمل كبيرًا، تخيل أنك كل فترة مضطر إلى تعديل الشيفرة التي كتبتها عند نزول كل تحديث، فهذا مرهق ومضيعة للوقت وذلك فقط للحفاظ على توافق مكونات إطار العمل.
</p>

<p>
	في إطار عمل Angular تكون <a href="https://angular.io/guide/releases" rel="external nofollow">التحديثات</a> الرئيسية مجدولة كل ستة أشهر. هناك أيضًا فترة ستة أشهر أخرى قبل إهمال أي واجهات برمجة تطبيقات رئيسية، مما يمنحك وقتًا كبيرًا نسبيًا (سنة كاملة) لإجراء التغييرات اللازمة -إن وجدت- وتعديل الشيفرة بما يتناسب مع التحديث.
</p>

<p>
	في الحقيقة عندما يتعلق الأمر بالاستقرار فإن React هي المسيطرة إذ <a href="https://reactjs.org/docs/design-principles.html#stability" rel="external nofollow">ذكرت</a> شركة فيسبوك بأن الاستقرار له أهمية قصوى بالنسبة لهم، لأن العديد من الشركات الكبرى تستخدم هذا الإطار، وعادةً ما تكون الترقيات من خلال الإصدارات هي الأسهل في إطار العمل React، إذ تساعدك السكربتات البرمجية مثل سكربت <a href="https://github.com/reactjs/react-codemod" rel="external nofollow">react-codemod</a> على تحديث الشيفرة البرمجية بسهولة.
</p>

<p>
	أما بالنسبة لإطار العمل Vue.js فيمكن اعتبارها الأسهل وهذا ما نلاحظه في قسم <a href="https://v3.vuejs.org/guide/migration/introduction.html" rel="external nofollow">تحديث</a> الشيفرة البرمجية في توثيق Vue 3 إذ <a href="https://vuejs.org/v2/guide/migration.html" rel="external nofollow">أشار</a> إلى أن هناك الكثير من التشابه بين Vue 2 و Vue 3 بينما 90٪ من واجهة برمجة التطبيقات هي نفسها وإذا أردت تحديث الشيفرة البرمجية من الإصدار 1.x إلى الإصدار 2.x فهناك أداة <a href="https://github.com/vuejs/vue-migration-helper" rel="external nofollow">مساعدة</a> لتحديث الشيفرة والتي تعمل على وحدة التحكم لتقييم حالة التطبيق الخاص بك.
</p>

<h3>
	فعالية إطار العمل
</h3>

<p>
	سنستعرض فعالية أُطر العمل من خلال أهم الخصائص التي نحتاجها في كل مشروع، من بينها الحجم الكلي للشيفرة البرمجية وأوقات التحميل، والمكونات المتاحة، ومنحنى صعوبة التعلم.
</p>

<h4>
	الحجم وأوقات التحميل
</h4>

<p>
	تعد أحجام الأطر صغيرة نسبيًا ولن تكون ذات تأثير كبير نظرًا لأن عمليات التخزين المؤقت Caching وعمليات التصغير Minification تُعدان من بديهيات التطوير والبرمجة في الوقت الحاضر. بالرغم من ذلك هنالك اختلاف كبير بين أحجام الأطر (على سبيل المثال Angular هي الأكبر حجمًا)، إلا أنها لا تزال صغيرة بالمقارنة بمتوسط حجم صفحة الويب (حوالي 2 ميجابايت وفقًا <a href="https://httparchive.org/reports/page-weight?start=2020_01_01&amp;end=latest&amp;view=list" rel="external nofollow">لبيانات</a> موقع httparchive). بالإضافة إلى ذلك فإذا كنت تستخدم <a href="https://academy.hsoub.com/devops/networking/%D8%B4%D8%A8%D9%83%D8%A7%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%AA%D9%88%D9%89-content-distribution-networks-r569/" rel="">شبكات توزيع المحتوى CDN</a> لهذه المكتبات فمن المحتمل جدًا أن يكون المستخدم نزّل بالفعل المكتبة في نظامه المحلي أثناء تحميل الموقع. في الحقيقة مع وجود الجيل الرابع والخامس من الاتصالات واتصالات الألياف الضوئية فايبر لن تكون هذه النقطة ذات أهمية ولذلك لن نتوسع بشرحها.
</p>

<h4>
	المكونات
</h4>

<p>
	المكونات هي من أهم الأجزاء في جميع الأطر الثلاثة، بغض النظر عما إذا كنا نتحدث Vue أو React أو Angular. يحصل المكون عمومًا على مدخلات، ويغير السلوك بناءً عليه. يظهر هذا التغيير في السلوك عمومًا كتغيير في واجهة المستخدم لجزء من الصفحة. يسهل استخدام المكونات عملية إعادة استخدام التعليمات البرمجية. يمكن أن يكون المكون عبارة عن سلة مشتريات على موقع التجارة الإلكترونية أو مربع تسجيل الدخول على شبكة اجتماعية.
</p>

<h5>
	المكونات في إطار العمل Angular
</h5>

<p>
	يتعامل إطار العمل Angular مع الوحدات modules والتي بدورها تضم <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="">المكونات</a>، وتُعَدّ المكوِّنات أحجار البناء الأساسية قي تطبيق Angular، فيحتوي المكوِّن على صنف TypeScript مع مزخرِف <code>‎</code>@Component()‎ وقالب <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> وملف التنسيقات، حيث يعرِّف مزخرِف <code>‎@Component()‎</code> المعلومات التالية في Angular:
</p>

<ul>
	<li>
		محدِّد CSS الذي يعرِّف الطريقة التي سيُستخدَم بها المكوِّن ضمن قالب ما، حيث تصبح عناصر HTML التي تستخدمها ضمن القالب الخاص بك والتي تطابق هذا المحدِّد نسخةً من هذا المكوِّن.
	</li>
	<li>
		قالب HTML الذي يوجّه Angular إلى كيفية إخراج هذا المكوِّن.
	</li>
	<li>
		مجموعة اختيارية من تنسيقات <a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> التي تعرِّف المظهر الخاص بعناصر قالب HTML.
	</li>
</ul>

<h5>
	المكونات في إطار العمل React
</h5>

<p>
	يجمع إطار العمل React المكونات بطريقة مثيرة للاهتمام بين واجهة المستخدم وسلوك المكونات في نفس الشيفرة البرمجية لتكون مسؤولةً عن إنشاء عنصر واجهة المستخدم وإملاء سلوكه (يمكنك الاطلاع على مقال <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-react-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-react-components-r834/" rel="">مكونات React الأساسية (React Components)</a> لمزيد من المعلومات عن المكونات في إطار العمل React).
</p>

<h5>
	المكونات في إطار العمل Vue
</h5>

<p>
	عند النظر إلى Vue نجد أن واجهة المستخدم والسلوك أيضًا جزءًا من المكونات بطريقة مشابهة لمكونات React، مما يجعل التعامل مع المكونات أكثر سهولة. كما ستجعل الأمور قابل للتخصيص بدرجة كبيرة، مما يسمح للمبرمج بدمج واجهة المستخدم وسلوك المكونات من داخل الشيفرة البرمجية. علاوة على ذلك، يمكنك أيضًا استخدام المعالجات المسبقة <a href="https://vue-loader.vuejs.org/guide/pre-processors.html" rel="external nofollow">pre-processor</a> في Vue بدلًا من <a href="https://academy.hsoub.com/programming/css/%d8%aa%d8%b9%d8%b1%d9%91%d9%81-%d8%b9%d9%84%d9%89-%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d8%a7%d8%aa-css-r70/" rel="">CSS</a>، وهذا أمر رائع. كما يعد إطار العمل Vue رائعًا عندما يتعلق الأمر بالتكامل مع المكتبات الأخرى، مثل <a href="https://wiki.hsoub.com/Bootstrap" rel="external">Bootstrap</a>.
</p>

<h3>
	صعوبة التعلم
</h3>

<p>
	من أهم الأسئلة التي ترد في ذهن المتعلم هو مدى صعوبة تعلم كل من هذه الأطر؟ سنحاول الإجابة على هذا السؤال.
</p>

<p>
	يكون لإطار العمل Angular منحنى تعليمي حاد وصعب التعلم نسبيًا مع الأخذ بعين الاعتبار أنه حل كامل ومتكامل، ولإتقان Angular يتطلب منك تعلم المفاهيم المرتبطة به مثل <a href="https://wiki.hsoub.com/TypeScript" rel="external">TypeScript</a> و MVC. في الحقيقة وبالرغم من أن تعلم Angular يستغرق وقتًا، إلا أن الاستثمار فيه يؤتي ثماره ولأن المتعلم سيفهم تمامًا كيفية عمل الواجهة الأمامية لتطبيقات الويب.
</p>

<p>
	يعد إطار عمل React أسهل نسبيًا ومع المرجع الرسمي الخاص به من شأنه أن يساعد الشخص في إعداد React في غضون ساعة تقريبًا. الوثائق شاملة وكاملة، مع حلول للمشكلات الشائعة الموجودة بالفعل على موقع Stack Overflow. لا يُعد React إطار عمل كاملًا مثل Angular، وتتطلب الميزات المتقدمة استخدام مكتبات خارجية. هذا يجعل منحنى التعلم الخاص بإطار العمل الأساسي ليس شديد الانحدار ولكنه يعتمد على المسار الذي تسلكه وتطبيق الويب الذي تريد بناءه. ومع ذلك، فإن التعلم السريع لاستخدام React لا يعني بالضرورة أنك تستخدم أفضل الممارسات (يمكنك الاطلاع على <a href="https://wiki.hsoub.com/React" rel="external">التوثيق الإرشادي الكامل لإطار React</a> باللغة العربية من خلال <a href="https://wiki.hsoub.com/%D8%A7%D9%84%D8%B5%D9%81%D8%AD%D8%A9_%D8%A7%D9%84%D8%B1%D8%A6%D9%8A%D8%B3%D9%8A%D8%A9" rel="external">موسوعة حسوب</a>).
</p>

<p>
	يعد إطار العمل Vue أسهل في التعلم من إطار Angular أو React. علاوة على ذلك، تتداخل Vue مع Angular و React فيما يتعلق بوظائفها مثل استخدام المكونات. ولذلك فإن الانتقال إلى Vue من أي منهما يعد خيارًا سهلًا. ومع ذلك، فإن بساطة ومرونة إطار عمل Vue هي سيف ذو حدين - فهي تسمح بكتابة شيفرة برمجية سهلة البنية بهدف التبسيط ولكن هذا الأمر الذي يمكن يجعل المطورين يُهملون إعداد بنية قوية للشيفرة البرمجية الأمر الذي يصعب عملية توسعة الشيفرة البرمجية أو تصحيح الأخطاء والاختبار لاحقًا عندما يتوسع المشروع.
</p>

<p>
	وعمومًا بالرغم من أن Angular و React أصعب نسبيًا، إلا أن استخداماتها عند الإتقان لا حدود لها. على سبيل المثال، يمكنك دمج Angular أو React مع <a href="https://academy.hsoub.com/apps/web/wordpress/" rel="">ووردبريس WordPress</a> و <a href="https://academy.hsoub.com/apps/web/wordpress/woocommerce/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-woocommerce-%D9%85%D9%86%D8%B5%D8%A9-%D8%A7%D9%84%D8%AA%D8%AC%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A%D8%A9-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D9%88%D9%88%D8%B1%D8%AF%D8%A8%D8%B1%D9%8A%D8%B3-r95/" rel="">WooCommerce</a> لإنشاء <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%85%D9%8A%D8%A9-pwa-r832/" rel="">تطبيقات ويب تقدمية PWA</a>.
</p>

<h2>
	أشهر الشركات التي تستخدم هذا الأطر
</h2>

<p>
	سنستعرض أهم الشركات التي تستخدم هذه الأطر لنرى كيفية تبني هذه التكنولوجيا ونظرة العمالقة لهذه التحديثات.
</p>

<h3>
	أشهر الشركات التي تستخدم إطار عمل Angular
</h3>

<p>
	تستخدم شركة غوغل إطار العمل Angular لأنه يوفر لها أداءً عاليًا للوظائف ولإنشاء تطبيقات أكبر. كما يستخدم في تطوير Google Cloud Console. كان Microsoft Office قادرًا على إطلاق تطبيقين مستقلين بمساعدة إطار العمل Angular أما مع حالة شركة Upwork فإنها تستخدمه لتوفير تجربة سريعة الاستجابة لأعضاء الموقع.
</p>

<h3>
	أشهر الشركات التي تستخدم إطار عمل React
</h3>

<p>
	في الحقيقة الكثير من الشركات الكبيرة تستخدم React وهذا دليل قوي على مدى جودة إطار العمل. كما يستخدم هذا الإطار عادة لما يوفره من طريقة أسرع لإنشاء تطبيقات الهاتف المحمول وصيانتها، مع استخدام موارد قليلة بالمقارنة مع طرق برمجة التطبيقات الأصيلة.
</p>

<p>
	البعض يعدها من أفضل المكتبات لتطبيقات الواجهة الأمامية الموجودة في السوق حاليًا. ومن هذه الشركات التي تستخدم هذا الإطار نذكر تويتر Twitter وأمازون برايم Amazon Prime ويوديمي Udemy وإكسبو Expo وأوبر Uber وبنتريست Pinterest ونيتفليكس Netflix وصحيفة نيويوك تايمز New York Times والكثير من الشركات الأخرى.
</p>

<h3>
	الشركات التي تستخدم إطار عمل Vue.js
</h3>

<p>
	يعد إطار عمل Vue.js حديثًا نسبيًا ولكن هناك العديد من الشركات التي تستخدمه. ومن بعض الشركات التي تستخدم Vue نذكر شركة غوغل وآبل وتريفاغو Trivago ونينتندو Nintendo.
</p>

<p>
	تمكنت شركة غوغل من استخدام Vue لصفحة الوظائف الخاصة بها، بينما أضافت شركة نينتندو إطار العمل في موقعها على الويب، وأضاف موقع تريفاغو Trivago وهو أحد أشهر المواقع للبحث عن الفنادق في العالم إطار العمل Vue في مجلتهم. واستخدمت شركة آبل Apple إطار Vue لإنشاء دروس SwiftUI الخاصة بهم. بالرغم من حداثة هذا الإطار إلا أن الشركات وجدت فيه السهولة والفعالية المناسبة لإضافته إلى تقنياتها.
</p>

<p>
	الجدير بالذكر أن إطار عمل Vue.js غالبًا ما تستخدمه الشركات الصينية العملاقة مثل علي بابا Alibaba وبيدو Baidu وتنسنت Tencent وحتى شركة شاومي Xiaomi وغيرها الكثير، بدلًا من الأطر React أو Angular التي أنشأتها شركات غربية مثل فيسبوك وغوغل. لا نعرف تمامًا سبب هذا التوجه ولكن يُتوقع بأن يستمر السوق الصيني في النمو بسرعة باستخدام إطار العمل Vue خاصة لأنها مكتبة مستقلة مفتوحة المصدر دون أي ارتباط خارجي.
</p>

<h2>
	كيف نختار الإطار الأنسب لمشروعنا؟
</h2>

<p>
	وهنا نأتي لأهم الأسئلة التي تخطر على بالنا وهي كيف نعرف أي الأطر مناسب لمشروعنا؟ وما هي أفضل طريقة لتعظيم فوائد كل إطار؟ كيف تعرف متى نستخدمها بناءً على مزايا وعيوب كل منها؟ إليك دليل سريع لمساعدتك على اتخاذ مثل هذه القرارات.
</p>

<h3>
	متى نستخدم إطار العمل Angular
</h3>

<p>
	بغض النظر عن شعبية وشهرة كل إطار يعد Angular الأكثر نضجًا بين الأطر، وله دعم جيد من المساهمين وهو حزمة متكاملة ومع ذلك، فإن صعوبة تعلم الإطار ومفاهيم التطوير في إطار Angular عمومًا يمكن أن تؤدي إلى إعاقة المطورين الجدد، وعادة ما يستخدم في المشاريع التالية:
</p>

<ul>
	<li>
		المشاريع الكبيرة والمعقدة.
	</li>
	<li>
		المشاريع القابلة للتوسع والموثوقة والمستقرة.
	</li>
	<li>
		المشاريع التي يمتلك مطوروها معرفة مسبقة بلغة TypeScript أو أنهم يستخدمونها بالفعل.
	</li>
</ul>

<h3>
	متى نستخدم إطار العمل React
</h3>

<p>
	يعد إطار العمل React الأكثر شيوعًا واستخدامًا في سوق العمل، ويبدو أن لهذا الإطار مستقبلًا مشرقًا. ويعد خيارًا جيدًا لشخص يريد بدء استخدام أطر عمل جافاسكربت للواجهة الأمامية والشركات الناشئة والمطورين الذين يحبون بعض المرونة. كما أن الإطار يمتلك القدرة على الاندماج مع الأطر الأخرى بسلاسة مضيفًا لنفسه ميزة كبيرة في أعين المبرمجين الذين يرغبون في بعض المرونة في الشيفرات البرمجية الخاصة بهم، وعمومًا يعد إطار العمل React حلًا مناسبًا للمشاريع التالية:
</p>

<ul>
	<li>
		المشاريع التي يمكن أن تتضمن مكونات قابلة لإعادة الاستخدام
	</li>
	<li>
		المشاريع البسيطة والمتوسطة التي تريد أن تنطلق فيها بسرعة
	</li>
	<li>
		المشاريع التي تتطلب مستوى عالٍ من قابلية التوسع
	</li>
	<li>
		المشاريع ذات مواعيد التسليم القصيرة
	</li>
</ul>

<h3>
	متى نستخدم إطار العمل Vue.js
</h3>

<p>
	هو الأحدث في الساحة وبدون أي دعم من شركة كبرى، ومع ذلك نجد بأنه يحقق أداءً جيدًا في السنوات القليلة الماضية ليخرج كمنافس قوي للأطر القديمة، وخاصة مع إصدار Vue 3.0. ربما بسبب اعتماد الكثير من العمالقة الصينيين على هذا الإطار، وعمومًا يمكنك استخدام إطار العمل Vue.js للمشاريع التالية:
</p>

<ul>
	<li>
		المشاريع والتطبيقات التي تحتاج لأداء عالي
	</li>
	<li>
		عندما تفتقر إلى مطوري الواجهة الأمامية المهرة
	</li>
	<li>
		فريقك لديه الوقت الكافي للتعرف على التكنولوجيا الجديدة
	</li>
</ul>

<h2>
	الخاتمة
</h2>

<p>
	تعرفنا في هذا المقال على الفوارق بين أطر عمل جافاسكربت فبدأنا بسرد موجز مختصر لتاريخ كل منها وفهمنا الفارق بينها في العديد من النواحي مثل سوق العمل ومجتمع المطورين وإقبالهم على تبني كل إطار وتعمقنا في فهم فعالية كل إطار من خلال حجم المكتبات وتأثيرها على أوقات تحميل الصفحة والفرق بين المكونات لنستعرض بعدها أشهر الشركات التي تستخدم هذه الأطر ومتى نستخدم كل واحد منها بناءً على المشروع الذي نعمل عليه ومتطلباتك، وعمومًا من الضروري دائمًا إجراء البحث الخاص بك قبل اتخاذ القرار حول أي إطار أنسب لك، خاصةً إذا كنت ستعمل في مشروع تجاري وليس في مشروع شخصي.
</p>

<p>
	بالنسبة للمهندس الحقيقي، لا يوجد فرق جوهري في الإطار الذي يختاره، لأنه يستغرق بعض الوقت فقط للتعود على الإطار الجديد. كل إطار له إيجابياته وسلبياته، مما يعني أنه لا يجب بالضرورة أن يكون هناك خيار صحيح دائمًا، وإنما <strong>لكل مقام مقال</strong>.
</p>

<p>
	تذكر أن أكاديمية حسوب تحتوي على مقالات تعليمية متنوعة حول أطر عمل جافاسكربت تلك بالإضافة إلى مئات المقالات حول لغة جافاسكربت نفسها ننصحك باستكشافها وذلك بزيارة قسم <a href="https://academy.hsoub.com/programming/javascript/" rel="">لغة جافاسكربت</a>.
</p>

<h2>
	المصادر
</h2>

<ul>
	<li>
		مقال <a href="https://www.techmagic.co/blog/reactjs-vs-angular-vs-vuejs-what-to-choose-in-2020/" rel="external nofollow">?React vs Angular vs Vue.js — What Is the Best Choice in 2022</a> لكاتبته Romana Kuts.
	</li>
	<li>
		مقال <a href="https://www.codeinwp.com/blog/angular-vs-vue-vs-react/" rel="external nofollow">Angular vs React vs Vue: Which Framework to Choose</a> لكاتبه Shaumik Daityari.
	</li>
	<li>
		مقال <a href="https://zerotomastery.io/blog/tech-trends-showdown-react-vs-angular-vs-vue/" rel="external nofollow">Tech Trends Showdown?: React vs Angular vs Vue</a> لكاتبه Andrei Neagoie.
	</li>
	<li>
		تقرير موقع httparchive <a href="https://httparchive.org/reports/page-weight?start=2020_01_01&amp;end=latest&amp;view=list" rel="external nofollow">Report: Page Weight</a>.
	</li>
	<li>
		<a href="https://insights.stackoverflow.com/survey/2021" rel="external nofollow">تقرير</a> موقع Stackoverflow.
	</li>
</ul>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-angular-r1393/" rel="">مقدمة في مفاهيم Angular</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">مدخل إلى الواجهات البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-angular-%D9%88%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-firestore-r1409/" rel="">مقدمة في بناء تطبيقات الويب باستخدام إطار العمل Angular وقاعدة بيانات Firestore</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-react-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-react-components-r834/" rel="">مكونات React الأساسية (React Components)</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/files/22-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-vuejs/" rel="">أساسيات إطار العمل Vue.js</a> 
	</li>
</ul>
]]></description><guid isPermaLink="false">1596</guid><pubDate>Tue, 28 Jun 2022 15:08:00 +0000</pubDate></item><item><title>&#x645;&#x642;&#x627;&#x631;&#x646;&#x629; &#x628;&#x64A;&#x646; JavaScript &#x648; TypeScript</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-javascript-%D9%88-typescript-r1598/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/629750d4ad0d4_--JavaScript--Typescript.jpg.12598e7fcc64801a189ba28f46b3a438.jpg" /></p>
<p>
	منذ صعود الإنترنت وظهور مستعرض الويب الأول بواجهة مستخدم رسومية عام 1993 تغير شكل العالم ولم تعد المعلومة حكرًا على أحد، وانتقل كل شيء تقربيًا في العالم الحقيقي إلى العالم الافتراضي. كان متصفح الإنترنت هو الأداة القياسية للتفاعل مع هذا العالم الافتراضي ومع سرعة نمو شبكة الويب العالمية أصبحت متطلبات المواقع والمتصفحات تزداد يومًا بعد يوم الأمر الذي جعلها تتغير بوتيرة سريعة جدًا. كانت صفحات الإنترنت في بداية التسعينات بسيطة وثابتة وكانت التفاعل معها محدودًا جدًا ويفتقر إلى السلوك الديناميكي الأمر الذي جعل شركة نتسكيب تقرر إضافة لغة برمجية إلى المتصفح الخاص بها Navigator المشهور جدًا آنذاك وسلمت المهمة إلى مبرمجها الشاب بريندان إيش Brendan Eich وسرعان ما أطلق هذا المبرمج النشيط لغة برمجية تدعى <a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a> في سبتمبر 1995 والتي غيرت طريقة تعاملنا مع المتصفح بطريقة لا رجعة فيها.
</p>

<p>
	كان الناس يسارعون في الدخول إلى عالم الإنترنت واستكشافه وعندما شاهدت شركة مايكروسوفت تطور متصفح شركة نتسكيب لم يروقها الأمر، ومع قرارها بدخول أنظمة الحاسب من خلال نظام ويندوز قررت دخول سوق متخصص أكثر وهو سوق متصفحات الويب من خلال متصفح إنترنت إكسبلور Internet Explorer، استخدمت شركة مايكروسوفت لغة جافاسكربت في متصفحها، وسرعان ما نشبت حرب بين إنترنت إكسبلور والمتصفح Navigator على واجهة جافاسكربت، لتقوم شركة مايكروسوفت بعدها بإجراء هندسة عكسية لمترجم جافاسكربت في المتصفح Navigator وإنشاء مترجم خاص بها عام 1996 يسمى JScript. جاء ذلك جنبًا إلى جنب مع الدعم الأولي <a href="https://academy.hsoub.com/programming/css/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D8%B3%D9%8A%D9%82-css-r551/" rel="">للغة تنسيق الصفحات CSS</a> وملفات HTML. كان ذلك نقلة نوعية في صفحات الويب واختلفت اختلافًا ملحوظًا عن الصفحات المخصصة لمتصفح Navigator. هذه الاختلافات جعلت من الصعب على المطورين بناء مواقعهم لتعمل بصورة جيدة في كلا المستعرضين.
</p>

<p>
	في هذه الأثناء ومع رؤية مطوري الويب لهذه اللغة وإمكانياتها أصبحت اللغة حديث الساعة بين المجتمعات التقنية وزاد الاهتمام بها سواء من تطوير وإجراء أبحاث لتحسينها ومن الأبحاث التي غيرت مسار حياة اللغة هو بحث جيسي جيمس جاريت Jesse James Garrett عام 2005 والذي صاغ فيها مصطلح Ajax ووصف مجموعة من تقنيات كانت لغة جافاسكربت عمودها الفقري، وذلك بهدف إنشاء تطبيقات ويب يمكنها تنزيل بيانات في الخلفية أثناء التصفح، وتجنب الحاجة لإعادة تحميل الصفحة من جديد. أدى هذا إلى صعود نجم جافا سكريبت وبدء عصر النهضة بقيادة المكتبات مفتوحة المصدر والمجتمعات التي تشكلت من حولها. أُنشأت العديد من المكتبات الجديدة آنذاك مثل jQuery و Prototype و Dojo Toolkit و MooTools وغيرها.
</p>

<p>
	كان تطور لغة <a href="https://academy.hsoub.com/programming/javascript/" rel="">جافاسكربت</a> لافتًا ومفيدًا ومع أن الهدف الأساسي منها جعل الواجهات الأمامية للمواقع تفاعلية إلا أن محبة المطورين لها جعلهم ينقلونها للواجهات الخلفية للتطبيقات الإنترنت من خلال Node.js لتصبح بذلك لغة صالحة لتطوير الواجهات والأمامية والخلفية، ومع ازدياد اعتماد المطورين على لغة جافاسكربت في تطوير مواقعهم بدأت تظهر المشاكل لأن لغة جافاسكربت لم تكن مهيأة لهذا التعامل وخصيصًا مع المشاريع الكبيرة أو التطبيقات الضخمة الأمر الذي جعل من الصعب صيانة الشيفرة البرمجية أو حتى إعادة تصميم الشيفرة البرمجية Refactoring وهنا بدأت تظهر الحلول الجديدة ومن بينها حل سحري جاء على يد شركة مايكروسوفت بطرحها للغة <a href="https://wiki.hsoub.com/TypeScript" rel="external">TypeScript</a>.
</p>

<p>
	سنحاول في هذا المقال تسليط الضوء على أبرز الاختلافات بين لغة جافاسكربت JavaScript ولغة TypeScript وسنسعى لتجنب التحيز بينها ولكن بالتأكيد هناك لغة قوية في مجال معين وأخرى ضعيفة وهذا أمر طبيعي.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix" data-gramm="false">
		<p>
			<strong>ملاحظة</strong>: نفترض في هذا المقال بأن لديك معرفة أساسية بتقنيات تطوير الويب وكيفية عمل المواقع.
		</p>
	</div>
</blockquote>

<h2>
	نبذة مختصرة عن لغات جافاسكربت Javascript و TypeScript
</h2>

<p>
	لفهم سريع لهذه اللغات لا بد من الاطلاع أولًا على ماهيتها وأبرز خصائصها.
</p>

<h3>
	لغة جافاسكربت
</h3>

<p>
	لغة جافاسكربت JavaScript: وهي أشهر لغات البرمجة في العالم وتعد لغة عالية المستوى تساعد في إنشاء صفحات ويب تفاعلية وديناميكية. وهي من التقنيات الأساسية لتطبيقات الويب وتتميز بدرجة عالية المرونة من خلال الأنماط الديناميكية والمصرّف الفوري Just-in-time compilation.
</p>

<p>
	بالإضافة إلى ذلك فهي لغة متعددة النماذج نظرًا لقدرتها على دعم البرمجة الوظيفية Functional Programming وأنماط <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%A5%D8%AC%D8%B1%D8%A7%D8%A6%D9%8A%D8%A9/" rel="">البرمجة الإجرائية Procedural Programming</a> والبرمجة المبنية على الأحداث Event-driven Programming. تشغل لغة جافا سكريبت المواقع من جانب العميل (عندما تشغل الشيفرة البرمجية على متصفح المستخدم)، كما يوجد محركات مبنية لتصريف الشيفرات البرمجية للغة جافا سكريبت للسماح بالتطبيقات جافاسكربت من جانب الخادم (عندما تشغل الشيفرات البرمجية على خادم الويب وتخصص الاستجابة وفقًا لطلب كل مستخدم).
</p>

<p>
	أهم النقاط الرئيسة في لغة جافاسكربت JavaScript:
</p>

<ul>
	<li>
		لغة البرمجة الأكثر شيوعًا.
	</li>
	<li>
		لغة كاملة ومتعددة المنصات ومتعددة النماذج وديناميكية.
	</li>
	<li>
		التنفيذ من جانب العميل والخادم.
	</li>
	<li>
		تقنية التصريف والتحسين في الوقت المناسب JIT.
	</li>
	<li>
		التوافق مع جميع المتصفحات.
	</li>
	<li>
		طور بالأصل للشيفرات البرمجية الصغيرة.
	</li>
</ul>

<h3>
	لغة TypeScript
</h3>

<p>
	طور فريق مايكروسوفت البنية الأساسية للغة <a href="https://wiki.hsoub.com/TypeScript" rel="external">TypeScript</a> في عام 2010 تقريبًا، وكان جوهر ما بنوه والغرض طويل المدى واضحين على الفور في الاسم: يجب أن تجعل لغة TypeScript لغة جافاسكربت أكثر قابلية للإدارة. كان الكثير من العمالقة التقنيين في شركة مايكروسوفت موجودين في ذلك الفريق التأسيسي الداخلي، وهو مؤشر جيد على أن مايكروسوفت كانت جادة بشأن المشروع.
</p>

<p>
	صدرت النسخة الرسمية الأولى عام 2012 وأطلقت TypeScript كمشروع <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D9%85%D8%B5%D8%B7%D9%84%D8%AD-%D9%85%D9%81%D8%AA%D9%88%D8%AD-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-open-source%D8%9F-r885/" rel="">مفتوح المصدر </a>على Github. تعد مايكروسوفت الآن واحدة من أكبر المساهمين في العالم مفتوح المصدر. ومع ذلك، في ذلك الوقت، لا يزال عملاق البرمجيات يتمتع بسمعة ازدواجية تجاه البرمجيات مفتوحة المصدر، وبهذا القرار كان قادرًا على أن يكون قدوة في مجتمع التطوير من خلال تغيير ثقافة الشركة نحو المعايير المفتوحة. تبنى المجتمع التقني هذه اللغة واعجبوا بها ويعملون باستمرار على تحسينها وتطويرها منذ ذلك الحين.
</p>

<p>
	تعد <strong>لغة TypeScript</strong>: مجموعة شاملة من لغة جافاسكربت، تحقق نفس الأغراض والوظائف للغة جافاسكربت. إلا أنها بُنيت بالأساس للتعامل مع التطبيقات الكبيرة وتطويرها من خلال تمكين كتابة شيفرة برمجية قوية وتتضمن عناصر التحكم في أخطاء وقت الترجمة. بتعبير أدق، تعد TypeScript لغة برمجة تدعم أنواع المتغيرات الثابتة Static والديناميكية Dynamic، وتوفر أيضًا ميزات الوراثة Inheritance والأصناف Classes ومجالات رؤية المتغيرات وفضاء الأسماء Namespaces والواجهات Interfaces والاتحادات Unions (وهي أحد بُنى المعطيات مماثلة لوظيفة الهياكل Structure) والعديد من الميزات الحديثة الأخرى التي تجدها في البرمجة كائنية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه"><abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr></abbr>. كما يمكن استخدام اللغة لتطبيقات الويب من جانب العميل والخادم. علاوة على ذلك، فإنها تتوافق مع مكتبات جافاسكربت أيضًا.
</p>

<p>
	من أهم النقاط الرئيسة في لغة TypeScript:
</p>

<ul>
	<li>
		أشمل من جافاسكربت ولكنها متوافقة مع مكتبات جافاسكربت.
	</li>
	<li>
		يمكن للشيفرة البرمجية المكتوبة اتباع مبادئ البرمجة غرضية التوجه <abbr title="Object-Oriented Programming | البرمجة كائنية التوجه"><abbr title="Object-Oriented Programming | البرمجة كائنية التوجه">OOP</abbr></abbr> بطريقة أفضل من طريقة جافا سكريبت الأساسية.
	</li>
	<li>
		دعم الأنواع الثابتة.
	</li>
	<li>
		أسهل في التصحيح.
	</li>
	<li>
		توفر اللغة الأنماط الثابتة.
	</li>
	<li>
		يقدم دعم IDE الكامل.
	</li>
	<li>
		يمكن تحويل الشيفرة البرمجية الخاصة بك إلى شيفرة جافاسكربت.
	</li>
	<li>
		كشف الأخطاء قبل تنفيذ الشيفرة نظرًا لضبط الأنواع ووضوح الشيفرة المكتوبة
	</li>
</ul>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة JavaScript
		</p>

		<p class="banner-subtitle">
			تعلم البرمجة بلغة جافا سكريبت انطلاقًا من أبسط المفاهيم وحتى بناء تطبيقات حقيقية.
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/javascript-application-development/" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة JavaScript" src="https://academy.hsoub.com/learn/assets/images/courses/javascript-application-development.png">
	</div>
</div>

<h2>
	مقارنة بين JavaScript و TypeScript
</h2>

<p>
	في الحقيقة بالرغم من أن العنوان يوحي لوهلة بأن هاتين اللغتين تتنافسان على أخذ حصة من السوق إلا أن المدقق سيرى بأن هاتين اللغتين مكملاتٍ لبعضهما بعضًا ولا يتنافسان لأن لغة جافاسكربت هي الأصل ولولاها لما وجدت لغة TypeScript وبالمقابل كان للغة TypeScript الفضل في إمكانية استخدام لغة جافاسكربت في المشاريع الكبيرة وهدف هذه المقارنة تحديد الأنسب في مشروع.
</p>

<p>
	في البداية لنستعرض بعض الأمثلة عن الاختلافات بين الشيفرات البرمجية، انظر مثلًا تعريف المتغيرات في جافاسكربت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4270_6" style=""><span class="kwd">var</span><span class="pln"> message </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Hello Everyone"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">var</span><span class="pln"> num </span><span class="pun">=</span><span class="pln"> </span><span class="lit">14</span><span class="pun">;</span></pre>

<p>
	ويقابلها في TypeScript الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4270_8" style=""><span class="kwd">var</span><span class="pln"> message</span><span class="pun">:</span><span class="pln">string </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Hello Everyone"</span><span class="pln">
</span><span class="kwd">var</span><span class="pln"> num</span><span class="pun">:</span><span class="pln">number </span><span class="pun">=</span><span class="pln"> </span><span class="lit">14</span></pre>

<p>
	إذ يجب ضبط نوع المتغير بدقة.
</p>

<p>
	ومثال آخر متقدم، يكون استخدام الأصناف Classes في لغة جافاسكربت هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4270_10" style=""><span class="com">// -- JavaScript with Babel -- //</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Article</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    constructor</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// -- Babel compiled output -- //</span><span class="pln">
</span><span class="str">"use strict"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> _classCallCheck</span><span class="pun">(</span><span class="pln">instance</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Constructor</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!(</span><span class="pln">instance instanceof </span><span class="typ">Constructor</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TypeError</span><span class="pun">(</span><span class="str">"Cannot call a class as a function"</span><span class="pun">);</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

</span><span class="kwd">var</span><span class="pln"> </span><span class="typ">Article</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="typ">Article</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    _classCallCheck</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Article</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	أما في لغة TypeScript تكون على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4270_12" style=""><span class="com">// -- TypeScript -- //</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Article</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    name</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
    constructor</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// -- TypeScript compiled output -- //</span><span class="pln">
</span><span class="kwd">var</span><span class="pln"> </span><span class="typ">Article</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="com">/** @class */</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">function</span><span class="pln"> </span><span class="typ">Article</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Article</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}());</span></pre>

<p>
	مثال آخر لكيفية استخدام الوِحدات Modules في لغة جافاسكربت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4270_14" style=""><span class="com">// -- JavaScript with Babel -- //</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Article</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

</span><span class="com">// -- Babel compiled output -- //</span><span class="pln">
</span><span class="str">"use strict"</span><span class="pun">;</span><span class="pln">

</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">defineProperty</span><span class="pun">(</span><span class="pln">exports</span><span class="pun">,</span><span class="pln"> </span><span class="str">"__esModule"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  value</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> _classCallCheck</span><span class="pun">(</span><span class="pln">instance</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Constructor</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!(</span><span class="pln">instance instanceof </span><span class="typ">Constructor</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TypeError</span><span class="pun">(</span><span class="str">"Cannot call a class as a function"</span><span class="pun">);</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

</span><span class="kwd">var</span><span class="pln"> </span><span class="typ">Article</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="typ">Article</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  _classCallCheck</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Article</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

exports</span><span class="pun">.</span><span class="kwd">default</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Article</span><span class="pun">;</span></pre>

<p>
	أما في لغة TypeScript تكون على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4270_16" style=""><span class="com">// -- TypeScript -- //</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Article</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

</span><span class="com">// -- TypeScript compiled output -- //</span><span class="pln">
define</span><span class="pun">([</span><span class="str">"require"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"exports"</span><span class="pun">],</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">require</span><span class="pun">,</span><span class="pln"> exports</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"use strict"</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">defineProperty</span><span class="pun">(</span><span class="pln">exports</span><span class="pun">,</span><span class="pln"> </span><span class="str">"__esModule"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> value</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">});</span><span class="pln">
    </span><span class="kwd">var</span><span class="pln"> </span><span class="typ">Article</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="com">/** @class */</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">function</span><span class="pln"> </span><span class="typ">Article</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Article</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}());</span><span class="pln">
    exports</span><span class="pun">.</span><span class="kwd">default</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Article</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span></pre>

<h3>
	المجتمع والتطوير
</h3>

<p>
	من المجحف قليلًا مقارنة لغة موجودة في سوق العمل منذ أكثر من 20 سنة مع لغة حديثة لم يمضِ على وجودها الفعلي أكثر من 10 سنوات ولذلك من المنطقي أن نجد فرقًا كبيرًا بين محبة المجتمع التقني للغة جافا سكريبت ولغة TypeScript وهذا ما أثبتته <a href="https://octoverse.github.com" rel="external nofollow">الإحصائيات</a> الخاصة بموقع Github لعام 2021 والسيطرة واضحة للغة جافاسكربت على حساب كل اللغات ومن بينها لغة TypeScript.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="100382" href="https://academy.hsoub.com/uploads/monthly_2022_06/statistics.png.33b1a70c93d0b64ade3b3ba690bc0436.png" rel=""><img alt="statistics.png" class="ipsImage ipsImage_thumbnailed" data-fileid="100382" data-unique="wh4kbiivk" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_06/statistics.thumb.png.1599107e66e4f6e0503c00d9b1ab74e4.png"></a>
</p>

<p>
	بل إن الإحصائية المثيرة للاهتمام فعلًا هي <a href="https://insights.stackoverflow.com/survey/2021#most-loved-dreaded-and-wanted-webframe-want" rel="external nofollow">الإحصائية</a> الخاصة بموقع stackoverflow والتي جاء في ترتيبها الثالث لغة TypeScript كأكثر لغة يحب أن يتعلمها المطورون في سنة 2021.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="101431" href="https://academy.hsoub.com/uploads/monthly_2022_06/loved.png.85eaa49285dcdc8ebe82396f5545e5fe.png" rel=""><img alt="loved.png" class="ipsImage ipsImage_thumbnailed" data-fileid="101431" data-unique="dd1htvin1" style="" src="https://academy.hsoub.com/uploads/monthly_2022_06/loved.thumb.png.45a8922da3c520689669c2af984e5c6d.png"></a>
</p>

<h3>
	تصريف الشيفرة البرمجية Compilation
</h3>

<p>
	ليست هناك حاجة لتصريف الشيفرة البرمجية عند استخدام لغة جافاسكربت. نظرًا لأنها لغة مفسرة Interpreted Language، لا يمكن العثور على الأخطاء إلا أثناء وقت التشغيل. بمعنى آخر، يجب أولًا تشغيل الشيفرة على المتصفح من أجل اختباره واعتباره صالحًا أم لا. ولذلك يمكن أن يستغرق الأمر وقتًا طويلًا للعثور على المشاكل والأخطاء في الشيفرة البرمجية.
</p>

<p>
	من ناحية أخرى تحتوي لغة TypeScript على ميزة خطأ وقت تصريف الشيفرة البرمجية Just In Time Compilation وهي عملية تدقيق الشيفرة البرمجية عند تصريفها. ويمكن أن تكتشف هذه العملية أخطاء في كتابة الشيفرة في حال لم يتبع المبرمج الصياغة الصحيحة والدلالات للغة TypeScript. هذه الميزة توفر للمطورين وقتًا ثمينًا قبل نشر وتشغيل تطبيقات الويب على الإنترنت. علاوة على ذلك، فإنها تساعد الشركات أيضًا على التخلص من الأخطاء في وضعية التطوير قبل الانتقال إلى وضعية الإنتاج للشيفرة البرمجية؛ أي أنها تحقق الفائدة للمطورين وأصحاب الشركات على حد سواء.
</p>

<h3>
	أنواع المتغيرات
</h3>

<p>
	تحتوي جافاسكربت على الأنواع ديناميكية فعلى سبيل المثال يمكن أن يكون المتغير الآن عددًا صحيحًا وبعد ذلك على سلسلة. هذا يجعل من الصعب معرفة كيفية التعامل مع ما يوجد داخل متغير معين. علاوة على ذلك، لا يوفر الكتابة الثابتة. تعني الكتابة الثابتة أن المطور يعلن عن نوع البيانات التي يمكن أن يمتلكها المتغير. فإذا صرح المبرمج عن متغير وليكن "x" ليحوي فقط الأعداد الصحيحة، فسيظهر المترجم خطأ إن أسندنا قيمة سلسلة نصية لذاك المتغير في الشيفرة.
</p>

<p>
	ربما تكون الأنماط الثابتة الميزة الرئيسية لاستخدام TypeScript. لأنها تسمح للمطور بالتحقق من دقة النمط أثناء وقت الترجمة الشيفرة البرمجية. على سبيل المثال توفر جافاسكربت الأنماط الأساسية للغة مثل السلسلة string والرقم number، لكنها لا تتحقق من صحة تعيين المطور لقيم هذه العناصر أثناء البرمجة. ولكن لغة TypeScript تفعل ذلك.
</p>

<p>
	علاوة على ذلك، فإن استخدام الأنماط الثابتة في لغة TypeScript في بيئات التطوير الحديثة (مثل بيئة برنامج VS Code) يمكن أن يقدم معلومات إضافية واقتراحات صحيحة حول الشيفرة البرمجية المكتوبة مما يساهم في توثيق أفضل للشيفرة (وهو ما يقدره المطورون الآخرون أيضًا). يعد التنقل في التعليمات البرمجية وإعادة البناء من الميزات المتاحة التي يمكن أن تساعد المطور على تتبع مكان وجود والتوابع، وما إلى ذلك. بالإضافة إلى ذلك تجعل لغة TypeScript من عملية اكتشاف المشاكل والأخطاء عملية سهلة.
</p>

<h3>
	نوعية لغة البرمجة
</h3>

<p>
	أضافت الرابطة الأوروبية لمصنعي الحاسوب ECMAScript معاييرًا محددة للغات البرمجة من خلال توفيرها قواعد وإرشادات وتفاصيل أخرى تصف ما يجب أن تتضمنه لغة برمجة مفسّرة. جافاسكربت هي لغة برمجة مفسّرة تتوافق مع مواصفات ECMAScript. يمكن أن تتغير هذه المواصفات، ويمكن إدخال مواصفات جديدة؛ وبالتالي هناك العديد من إصدارات ECMAScript. كان ECMAScript 6 أحد الإصدارات التي قدمت أبرز التعديلات (والمعروف أيضًا باسم ES6 أو ECMAScript 2015). إذ قدم هذا الإصدار الوِحدات، والصفوف، والدوال السهمية، وخصائص محسّنة للكائنات، وميزات أخرى ممتازة.
</p>

<p>
	ولكن السؤال الوجيه هل لغة جافاسكربت <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-object-oriented-javascript-r179/" rel="">كائنية التوجه</a> Object-Oriented Programming؟ في الحقيقة وبالرغم من إصدار مفهوم الأصناف إلا أن صياغة الشيفرة البرمجية هي وراثة النموذج الأولي Prototypal inheritance أي أن لغة جافاسكربت معتمدة على طريقة النموذج الأولي Prototype-based، وليس على طريقة الصفوف Class-based. ولذلك لا تعد لغة جافاسكربت لغة كائنية التوجه فقط بالرغم من قدرتها على اتباع بعض مبادئها.
</p>

<p>
	ولكن ماذا عن لغة TypeScript هل هي كائنية التوجه؟ من الصعب الإجابة على هذا السؤال بطريقة مباشرة لأن لغة TypeScript تحتوي على الصفوف Classes وميزات أخرى تسمح للمطور باتباع تقنيات ومبادئ البرمجة غرضية التوجه ولكنها لا تجبر المطور على اتباع هذه المبادئ مثلما تفعل بعض اللغات الأخرى (مثل لغة Java ولغة C#‎). لذلك لا تعد TypeScript عادة لغة برمجة موجهة للكائنات فقط.
</p>

<p>
	في الواقع، بدلًا من التعليمات البرمجية الموجهة للكائنات في TypeScript، يمكن للمطور أيضًا اختيار التعليمات البرمجية الإجرائية Imperative Programming أو الوظيفية Functional Programming. وبالتالي نستنتج بأن كلًا من جافاسكربت و TypeScript لغات متعددة النماذج.
</p>

<h2>
	أي اللغتين أفضل للمبتدئين؟
</h2>

<p>
	في البداية يجب تعلم جافاسكربت أولًا وكلما فهم المطورون معلومات أكثر عن لغة جافاسكربت سيكون من السهل عليهم الانتقال إلى لغة TypeScript لأن اللغتين تشتركان في نفس بناء الجملة وكذلك نفس سلوك وقت التشغيل (باستثناء حقيقة أن لغة TypeScript لديها مدقق وقت التصريف).
</p>

<p>
	تمتلك لغة جافاسكربت الكثير من الموارد المتاحة بالإضافة إلى مجتمعها الضخم باعتبارها اللغة الأكثر شيوعًا حاليًا وفي معظم الحالات، يمكن لمطوري TypeScript أيضًا الاستفادة من هذه الموارد لأن الطريقة التي تنفذ بها المهام ستكون هي نفسها.
</p>

<h2>
	هل لغة جافاسكربت JavaScript أفضل من TypeScript
</h2>

<p>
	بعد النظر في الاختلافات الرئيسية بين اللغتين يبدو أن الأمر لن يستغرق وقتًا طويلًا حتى تلحق لغة TypeScript بلغة جافاسكريبت في سباق الشعبية والتطوير، ولكن السؤال الأهم أيهما أفضل؟
</p>

<p>
	الأمر يعتمد على حجم المشروع وعمومًا بالنسبة للمشروعات الصغيرة لا تبرز أهمية لغة TypeScript حقًا ويمكن ألا تستحق الجهد المبذول في التعلم. في هذه الحالة، تكون جافاسكربت أكثر فائدة لأنها تعمل في كل مكان (عبر الأنظمة الأساسية) وخفيفة على المتصفح. في الواقع، أحد عيوب TypeScript، بالمقارنة مع جافاسكربت هي أنها لا يعمل على كل المتصفحات مما يعني أنه يجب استخدام مترجم TypeScript أو محول الشيفرات Babel لتحويل شيفرة TypeScript إلى شيفرة جافاسكربت عادية مما يجعلها مفهومًة لجميع المتصفحات.
</p>

<p>
	بالإضافة إلى ذلك، يمكن للغة جافاسكربت أن تعمل بصورة أسرع، بالرغم من عدم ملاءمتها للتطبيقات الأكبر والأكثر تعقيدًا. كما أن لغة TypeScript تستغرق بعض الوقت وموارد وحدة المعالجة المركزية لترجمة التعليمات البرمجية، على عكس لغة جافاسكربت فإنها لا يعرض التغييرات في المتصفح على الفور (يستغرق بضع ثوانٍ).
</p>

<p>
	أولًا في TypeScript أسهل لإعادة بناء التعليمات البرمجية. ثانيًا تعتمد اعتمادًا كبيرًا على الأنواع الواضحة، مما يمكّن المطورين والفرق من فهم كيفية تفاعل الأجزاء المختلفة بصورة أفضل. أخيرًا وليس آخرًا، تحدد TypeScript المشاكل والأخطاء الأخرى عن طريق فحص وقت الترجمة. يمكن لهذه الميزات تحسين الكفاءة والتنظيم عند العمل في أنظمة واسعة النطاق وفريق كبير من المبرمجين.
</p>

<p>
	وعمومًا تعد لغة TypeScript مشابهةً جدًا للغة جافا سكريبت ويمكننا استخدامها مع جميع المكتبات والأدوات والأطر التي تمتلكها جافاسكربت، لذلك من الجدير بالتأكيد تجربته على TypeScript عندما يتعلق الأمر بمشاريع أكثر تعقيدًا.
</p>

<h2>
	مصادر لتعلم لغة جافا سكريبت ولغة TypeScript
</h2>

<p>
	وفرت موسوعة حسوب ترجمةً عربيةً لتوثيق <a href="https://wiki.hsoub.com/JavaScript" rel="external">لغة جافاسكربت</a> وأيضًا لتوثيق <a href="https://wiki.hsoub.com/TypeScript" rel="external">لغة TypeScript</a> يمكنك الرجوع إليهما وقتما تشاء وإضافتهما مصدرًا لتعلم هاتين اللغتين، كما تجد في أكاديمية حسوب قسمًا عن <a href="https://academy.hsoub.com/programming/javascript/" rel="">لغة جافاسكربت</a> يحوي مئات المقالات التعليمية المفيدة بمختلف المستويات بدءًا من المستوى المبتدئ وحتى المتقدم كما تجد أيضًا مقالات تعليمية مفيدة في قسم <a href="https://academy.hsoub.com/programming/javascript/typescript/" rel="">لغة TypeScript</a>.
</p>

<p>
	وأنصحك إن كنت مبتدئًا في جافاسكربت بقراءة سلسلة مقالات <a href="https://academy.hsoub.com/search/?tags=%D8%AF%D9%84%D9%8A%D9%84%20%D8%AA%D8%B9%D9%84%D9%85%20%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA&amp;sortby=newest&amp;page=1" rel="">دليل تعلم جافاسكربت</a> التي فيها أكثر من 90 مقالًا يتحدث عن لغة جافاسكربت بالكامل، وبعدها يمكنك الانتقال إلى سلسلة <a href="https://academy.hsoub.com/search/?tags=%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA%20%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9&amp;sortby=newest&amp;page=1" rel="">جافاسكربت متقدمة</a> لمستوى أكثر تقدمًا في اللغة وبذلك أضمن لك أن تصبح خبيرًا بلغة جافاسكربت وكل ذلك من مصدر عربي.
</p>

<h2>
	الخاتمة
</h2>

<p>
	تعرفنا في هذا المقال على التاريخ الموجز لجانب بسيط من لغة جافاسكربت وفهمنا أبرز المراحل التي مرت بها وكيف تطورت إلى هذه المرحلة التي نشهدها في وقتنا الحالي وفصلنا بعدها في فهم كل من لغة جافاسكربت و TypeScript وانتقلنا بعدها لنستطلع الفروقات بينها من ناحية الشعبية والتطوير وأنواع المتغيرات ونوع اللغة البرمجية، وناقشنا أيهما يجب على المبتدئ تعلمه في البداية وانتهينا بمعرفة اللغة المناسبة لكل مشروع.
</p>

<p>
	ختامًا إن لغة جافاسكربت لغة قوية جدًا وهي اللغة الأكثر شعبية لسنوات عديدة في إحصائيات موقع Stackoverflow. ومع ذلك هذا لا يعني أنها مثالية (وهل برأيك يوجد لغة برمجة مثالية؟). عندما يتعلق الأمر بالتعامل مع المشاريع الكبيرة، يمكن أن تصبح الأمور فوضوية ومربكة في جافاسكربت. لذلك طورت شركة مايكروسوفت لغة TypeScript لهذا الاستخدام تحديدًا. ما رأيك أنت عزيزي القارئ هل فعلًا أحدهما أفضل من الآخر؟ شاركنا رأيك في التعليقات.
</p>

<h2>
	المصادر
</h2>

<ul>
	<li>
		مقال <a href="https://www.imaginarycloud.com/blog/typescript-vs-javascript/" rel="external nofollow">?Typescript vs Javascript: Which one is better</a> لكاتبتيه Mariana Berga وRute Figueiredo.
	</li>
	<li>
		مقال <a href="https://www.guru99.com/typescript-vs-javascript.html" rel="external nofollow">?TypeScript vs JavaScript: What is the Difference</a> لكاتبه James Hartman.
	</li>
	<li>
		مقال <a href="https://www.monocubed.com/typescript-vs-javascript/" rel="external nofollow">TypeScript vs JavaScript: 7 Major Differences You Must Know</a> لكاتبه Jeel Patel.
	</li>
	<li>
		مقال <a href="https://geekflare.com/typescript-vs-javascript/" rel="external nofollow">Understanding Difference Between TypeScript and JavaScript</a> لمحرري الموقع.
	</li>
</ul>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/typescript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-typescript-r1214/" rel="">مدخل إلى TypeScript</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/typescript/%D8%A7%D9%84%D8%AE%D8%B7%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D9%88%D9%84%D9%89-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-typescript-r1215/" rel="">الخطوات الأولى في بناء تطبيقات الويب باستعمال TypeScript</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">الدليل السريع إلى لغة البرمجة جافاسكريبت JavaScript</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1598</guid><pubDate>Sun, 12 Jun 2022 15:00:00 +0000</pubDate></item><item><title>&#x645;&#x64A;&#x632;&#x627;&#x62A; &#x623;&#x637;&#x631; &#x627;&#x644;&#x639;&#x645;&#x644; &#x627;&#x644;&#x631;&#x626;&#x64A;&#x633;&#x64A;&#x629; &#x641;&#x64A; &#x62A;&#x637;&#x648;&#x64A;&#x631; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x645;&#x646; &#x637;&#x631;&#x641; &#x627;&#x644;&#x639;&#x645;&#x64A;&#x644;</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D9%8A%D8%B2%D8%A7%D8%AA-%D8%A3%D8%B7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D8%A7%D9%84%D8%B1%D8%A6%D9%8A%D8%B3%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1568/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_05/62823015a1b4b_---------.png.7a20c837a559bdb1b57544c1f9d29e4c.png" /></p>

<p>
	يملك كل إطار عمل جافاسكربت رئيسي نهجًا مختلف لتحديث نموذج كائن المستند <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">DOM</a>، ومعالجة أحداث المتصفح، وتوفير تجربة مطوِّر ممتعة، إذ سنستكشف في هذا المقال الميزات الرئيسية لأطر عمل "الأربعة الكبار"، وكيفية عمل هذه الأطر، والاختلافات بينها.
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت</a>.
	</li>
	<li>
		<strong>الهدف</strong>: فهم ميزات شيفرة أطر العمل Frameworks الرئيسية.
	</li>
</ul>
<h2>
	لغات المجال المحدد Domain-specific Languages
</h2>

<p>
	سنشغّل جميع أطر العمل التي سنناقشها في هذا المقال باستخدام لغة جافاسكربت، إذ ستتيح جميعها استخدام لغات المجال المحدد Domain-specific Languages -أو DSLs اختصارًا- لبناء التطبيقات، كما تستخدم مكتبة <a href="https://wiki.hsoub.com/React" rel="external">React</a> صيغة <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B5%D9%8A%D8%BA%D8%A9-javascript-syntax-extension-jsx-r777/" rel="">JSX</a> لكتابة مكوناتها، في حين يستخدِم إطار عمل Ember لغة Handlebars، كما تعرف هذه اللغات كيفية قراءة متغيرات البيانات على عكس لغة HTML، ويمكن استخدام هذه البيانات لتبسيط عملية كتابة واجهة المستخدِم؛ أما تطبيقات <a href="https://academy.hsoub.com/programming/javascript/angular/" rel="">Angular</a>، فتستخدِم لغة <a href="https://wiki.hsoub.com/TypeScript" rel="external">TypeScript</a> التي لا تهتم بكتابة واجهات المستخدِم ولكنها لغة مجال محدد، وتختلف كثيرًا عن لغة جافاسكربت الصرفة Vanilla JavaScript.
</p>

<p>
	لا يستطيع المتصفح قراءة لغات DSL مباشرةً، لذلك يجب تحويلها إلى لغة جافاسكربت أو HTML أولًا، إذ يُعَدّ التحويل خطوةً إضافيةً في عملية التطوير، ولكن تتضمن أدوات إطار العمل الأدوات المطلوبة لمعالجة هذه الخطوة، أو يمكن تعديلها لتضمين هذه الخطوة، كما يمكن إنشاء تطبيقات إطار عمل دون استخدام هذه اللغات، لكن سيبسّط استخدامها عملية التطوير ويسهّل العثور على المساعدة من مجتمعات تلك الأطر.
</p>

<h3>
	صيغة JSX
</h3>

<p>
	يرمز الاختصار JSX إلى لغتي جافاسكربت و XML، ويُعَدّ امتدادًا للغة جافاسكربت، إذ يضيف صيغةً تشبه لغة HTML إلى بيئة جافاسكربت، وقد اخترع فريق React صيغة JSX لاستخدامها في تطبيقات React، ولكن يمكن استخدامها لتطوير تطبيقات أخرى مثل تطبيقات <a href="https://academy.hsoub.com/programming/javascript/vuejs/" rel="">Vue</a>، وإليك مثال بسيط لصيغة JSX:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8337_12" style="">
<span class="kwd">const</span><span class="pln"> subject </span><span class="pun">=</span><span class="pln"> </span><span class="str">"World"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> header </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">header</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">subject</span><span class="pun">}!&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">header</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	يمثِّل التعبير السابق عنصر <code>&lt;header&gt;</code> في لغة HTML وبداخله عنصر <code>&lt;h1&gt;</code>، إذ تخبر الأقواس المعقوصة حول <code>subject</code> في السطر الرابع التطبيق بقراءة قيمة الثابت <code>subject</code> وإدخاله في العنصر <code>&lt;h1&gt;</code>، في حين ستُصرَّف صيغة JSX من جزء الشيفرة السابق عند استخدامها مع إطار عمل React إلى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8337_14" style="">
<span class="kwd">var</span><span class="pln"> subject </span><span class="pun">=</span><span class="pln"> </span><span class="str">"World"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">var</span><span class="pln"> header </span><span class="pun">=</span><span class="pln"> </span><span class="typ">React</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"header"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">React</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"h1"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Hello, "</span><span class="pun">,</span><span class="pln"> subject</span><span class="pun">,</span><span class="pln"> </span><span class="str">"!"</span><span class="pun">)</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	سينتج جزء الشيفرة السابق ما يلي في لغة HTML عندما يصيّره المتصفح في النهاية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8337_16" style="">
<span class="tag">&lt;header&gt;</span><span class="pln">
  </span><span class="tag">&lt;h1&gt;</span><span class="pln">Hello, World!</span><span class="tag">&lt;/h1&gt;</span><span class="pln">
</span><span class="tag">&lt;/header&gt;</span></pre>

<h3>
	لغة Handlebars
</h3>

<p>
	لا تُعَدّ لغة القوالب Handlebars لغةً خاصةً بتطبيقات Ember، ولكنها تُستخدَم بكثرة معها، إذ تشبه شيفرة Handlebars لغة HTML، ولكن لديها خيار سحب البيانات من مكان آخر، إذ يمكن استخدام هذه البيانات للتأثير على ملفات HTML التي يبنيها التطبيق في النهاية، كما تستخدِم لغة Handlebars -مثل صيغة JSX- الأقواس المعقوصة لحقن قيمة متغير، ولكنها تستخدِم زوجًا مزدوجًا من الأقواس المعقوصة بدلًا من زوج واحد، وإليك قالب Handlebars التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8337_18" style="">
<span class="tag">&lt;header&gt;</span><span class="pln">
  </span><span class="tag">&lt;h1&gt;</span><span class="pln">Hello, {{subject}}!</span><span class="tag">&lt;/h1&gt;</span><span class="pln">
</span><span class="tag">&lt;/header&gt;</span></pre>

<p>
	والبيانات التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8337_20" style="">
<span class="pun">{</span><span class="pln">
  subject</span><span class="pun">:</span><span class="pln"> </span><span class="str">"World"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستبني لغة Handlebars جزء HTML التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8337_22" style="">
<span class="tag">&lt;header&gt;</span><span class="pln">
  </span><span class="tag">&lt;h1&gt;</span><span class="pln">Hello, World!</span><span class="tag">&lt;/h1&gt;</span><span class="pln">
</span><span class="tag">&lt;/header&gt;</span></pre>

<h3>
	لغة TypeScript
</h3>

<p>
	تُعَدّ <a href="https://academy.hsoub.com/programming/javascript/typescript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-typescript-r1214/" rel="">لغة TypeScript</a> مجموعةً شاملةً من جافاسكربت، أي أنها توسّعها، إذ تُعَدّ كل شيفرات جافاسكربت صالحةً للغة TypeScript، ولكن العكس ليس صحيحًا، كما تُعَدّ لغة TypeScript مفيدةً للصرامة التي تسمح للمطورين بفرضها على شيفرتهم البرمجية مثل دالة <code>add()‎</code> التي تأخذ الأعداد الصحيحة <code>a</code> و<code>b</code> وتعيد ناتج جمعهما، ويمكن كتابة هذه الدالة في لغة جافاسكربت على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8337_26" style="">
<span class="kwd">function</span><span class="pln"> add</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	قد تكون هذه الشيفرة بسيطةً جدًا بالنسبة لشخص اعتاد على استخدام جافاسكربت، ولكنها يمكن أن تكون أوضح، إذ تتيح لنا لغة جافاسكربت استخدام المعامل <code>+</code> لربط السلاسل مع بعضها بعضًا، لذلك ستعمل هذه الدالة أيضًا إذا كان <code>a</code> و<code>b</code> عبارة عن سلاسل نصية Strings، وبالتالي قد لا تمنحك النتيجة التي تتوقعها، فإذا أردنا السماح فقط بتمرير الأعداد إلى هذه الدالة، فستجعل لغة TypeScript ذلك ممكنًا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8337_28" style="">
<span class="kwd">function</span><span class="pln"> add</span><span class="pun">(</span><span class="pln">a</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يخبر النوع <code>‎: number</code> المكتوب بعد كل معامِل في لغة TypeScript أنّ كلا المعامِلَين <code>a</code> و<code>b</code> يجب أن يكونا عددَين، فإذا أردنا استخدام هذه الدالة وتمرير القيمة <code>'2'</code> إليها بوصفها وسيطًا، فستعطي لغة TypeScript خطأً أثناء التصريف Compilation، وبالتالي سنضطر إلى إصلاح هذا الخطأ، كما يمكننا كتابة شيفرة جافاسكربت الخاصة بنا والتي تعطينا هذه الأخطاء، إلا أنها ستجعل شيفرتنا البرمجية أكثر تفصيلًا، إذ يمكن أن يكون السماح للغة TypeScript بمعالجة مثل هذه الفحوصات نيابةً عنا أمرًا منطقيًا.
</p>

<h2>
	كتابة المكونات
</h2>

<p>
	تحتوي معظم أطر العمل على نموذج مكونات، إذ يمكن كتابة مكونات React باستخدام صيغة JSX، ومكونات Ember باستخدام لغة Handlebars، ومكونات Angular وVue باستخدام صيغة القوالب التي توسّع لغة HTML قليلًا، كما توفِّر مكونات كل إطار عمل -بغض النظر عن كيفية كتابة المكونات- طريقةً لوصف الخاصيات الخارجية التي قد تحتاجها، والحالة الداخلية التي يجب أن يديرها المكوِّن، والأحداث التي يمكن للمستخدِم أن أن يتفاعل بها مع المكوِّن، كما سنعطي في هذا المقال أمثلةً من مقتطفات شيفرة React والتي ستُكتَب باستخدام صيغة JSX.
</p>

<h3>
	الخاصيات Properties
</h3>

<p>
	تُعَدّ الخاصيات Properties -أو props اختصارًا- بيانات خارجية يحتاجها المكوِّن من أجل تصييرها Render، ولنفترض أنك تنشئ موقعًا إلكترونيًا لمجلة على الإنترنت، وتحتاج إلى التأكُّد من أن كل كاتب مساهم يُنسَب له عمله، فيمكنك إنشاء مكوِّن <code>AuthorCredit</code> لكل مقال، إذ يحتاج هذا المكوِّن إلى عرض صورة شخصية للمؤلف وسطر قصير يحتوي على بعض المعلومات عنه، لذلك يحتاج المكوِّن <code>AuthorCredit</code> إلى قبول بعض الخاصيات من أجل معرفة الصورة المراد تصييرها والسطر القصير المطلوب طباعته، إذ يمكن أن يبدو تمثيل React للمكوِّن <code>AuthorCredit</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8337_32" style="">
<span class="pln">function AuthorCredit(props) {
  return (
    </span><span class="tag">&lt;figure&gt;</span><span class="pln">
      </span><span class="tag">&lt;img</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">{props.src}</span><span class="pln"> </span><span class="atn">alt</span><span class="pun">=</span><span class="atv">{props.alt}</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
      </span><span class="tag">&lt;figcaption&gt;</span><span class="pln">{props.byline}</span><span class="tag">&lt;/figcaption&gt;</span><span class="pln">
    </span><span class="tag">&lt;/figure&gt;</span><span class="pln">
  );
}</span></pre>

<p>
	تمثِّل {props.src} و{props.alt} و{props.byline} المكان الذي ستُدرَج فيه الخاصيات ضمن المكوِّن، إذ يمكن تصيير هذا المكوِّن من خلال كتابة الشيفرة التالية في المكان الذي نريده، والذي سيكون على الأرجح ضمن مكوِّن آخر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8337_35" style="">
<span class="pun">&lt;</span><span class="typ">AuthorCredit</span><span class="pln">
  src</span><span class="pun">=</span><span class="str">"./assets/zelda.png"</span><span class="pln">
  alt</span><span class="pun">=</span><span class="str">"Portrait of Zelda Schiff"</span><span class="pln">
  byline</span><span class="pun">=</span><span class="str">"Zelda Schiff is editor-in-chief of the Library Times."</span><span class="pln">
</span><span class="pun">/&gt;</span></pre>

<p>
	مما يؤدي في النهاية إلى تصيير عنصر <code>&lt;figure&gt;</code> التالي في المتصفح مع بنيته المحدَّدة في المكوِّن <code>AuthorCredit</code>، ومحتواه المحدَّد في الخاصيات المدرجة في استدعاء المكوِّن <code>AuthorCredit</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8337_37" style="">
<span class="pun">&lt;</span><span class="pln">figure</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">img
    src</span><span class="pun">=</span><span class="str">"assets/zelda.png"</span><span class="pln">
    alt</span><span class="pun">=</span><span class="str">"Portrait of Zelda Schiff"</span><span class="pln">
  </span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">figcaption</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="typ">Zelda</span><span class="pln"> </span><span class="typ">Schiff</span><span class="pln"> is editor</span><span class="pun">-</span><span class="pln">in</span><span class="pun">-</span><span class="pln">chief of the </span><span class="typ">Library</span><span class="pln"> </span><span class="typ">Times</span><span class="pun">.</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">figcaption</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">figure</span><span class="pun">&gt;</span></pre>

<h3>
	الحالة State
</h3>

<p>
	يُعَدّ وجود آلية قوية للتعامل مع الحالة مفتاحًا لإطار عمل فعّال، وقد يحتوي كل مكوِّن على بيانات يجب التحكم بحالتها، إذ ستستمر هذه الحالة بطريقة ما طالما أنّ المكوِّن قيد الاستخدام، ويمكن استخدام الحالة مثل الخاصيات للتأثير على كيفية تصيير المكوِّن، ولنفترض مثلًا وجود زر يحسب عدد مرات النقر فوقه، إذ يجب أن يكون هذا المكوِّن مسؤولًا عن تتبّع حالة العد <code>count</code> الخاصة به، ويمكن كتابته كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8337_39" style="">
<span class="kwd">function</span><span class="pln"> </span><span class="typ">CounterButton</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">count</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">button</span><span class="pun">&gt;</span><span class="typ">Clicked</span><span class="pln"> </span><span class="pun">{</span><span class="pln">count</span><span class="pun">}</span><span class="pln"> times</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُعَدّ <code>useState()‎</code> خطاف React الذي سيتتبع قيمة بيانات أولية أثناء تحديثها عند إعطائه تلك القيمة، وستُصيَّر الشيفرة بدايةً كما يلي في المتصفح:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8337_41" style="">
<span class="pun">&lt;</span><span class="pln">button</span><span class="pun">&gt;</span><span class="typ">Clicked</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> times</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span></pre>

<p>
	يتتبّع استدعاء الخطاف <code>useState()‎</code> الحالة <code>count</code> بطريقة قوية عبر التطبيق دون الحاجة إلى كتابة شيفرة لتنفيذ ذلك بنفسك.
</p>

<h3>
	الأحداث Events
</h3>

<p>
	تحتاج المكونات إلى طرق للاستجابة لأحداث المتصفح من أجل أن تكون تفاعلية، وبالتالي ستتمكن تطبيقاتنا من الاستجابة للمستخدِمين، إذ يوفّر كل إطار من أطر العمل صيغته الخاصة للاستماع إلى أحداث المتصفح، والتي تشير إلى أسماء أحداث المتصفح الأصيلة المكافِئة، إذ يتطلب الاستماع إلى حدث النقر <code>click</code> خاصيةً خاصةً هي <code>onClick</code> في React، ولنحدّث شيفرة <code>CounterButton</code> السابقة للسماح لها بحساب عدد النقرات كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8337_43" style="">
<span class="kwd">function</span><span class="pln"> </span><span class="typ">CounterButton</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">count</span><span class="pun">,</span><span class="pln"> setCount</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">button onClick</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setCount</span><span class="pun">(</span><span class="pln">count </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)}&gt;</span><span class="typ">Clicked</span><span class="pln"> </span><span class="pun">{</span><span class="pln">count</span><span class="pun">}</span><span class="pln"> times</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	استخدمنا دالة <code>useState()‎</code> إضافية لإنشاء دالة <code>setCount()‎</code> خاصة يمكن استدعاؤها لتحديث قيمة <code>count</code>، إذ نستدعي هذه الدالة في السطر الرابع، ونضبط قيمة <code>count</code> على قيمتها الحالية مع إضافة 1 إليها.
</p>

<h2>
	مكونات التنسيق Styling components
</h2>

<p>
	يوفِّر كل إطار من أطر العمل طريقةً لتحديد تنسيقات لمكوناتك أو للتطبيق كله، إذ توفِّر جميعها طرقًا متعددةً لتعريف تنسيقات المكوِّن على الرغم من اختلاف نهج كل إطار عن الآخر، ويمكنك تصميم تطبيقات إطار العمل باستخدام <a href="https://wiki.hsoub.com/Sass" rel="external">Sass</a> أو <a href="https://lesscss.org/" rel="external nofollow">Less</a>، أو تحويل Transpile ملفات تنسيقات CSS باستخدام <a href="https://postcss.org/" rel="external nofollow">PostCSS</a> مع إضافة بعض الوحدات المساعِدة.
</p>

<h2>
	التعامل مع الاعتماديات
</h2>

<p>
	توفِّر جميع الأطر الرئيسية آليات للتعامل مع الاعتماديات Dependencies باستخدام مكوِّنات ضمن مكوّنات أخرى وبمستويات هرمية متعددة في بعض الأحيان، وستختلف آليات هذه الإطارات عن بعضها بعضًا، ولكن النتيجة النهائية هي نفسها كما هو الحال مع الميزات الأخرى، كما تميل المكوّنات إلى استيراد مكوّنات في مكوّنات أخرى باستخدام صيغة وحدة جافاسكربت المعيارية أو شيء آخر مشابه.
</p>

<h3>
	مكونات ضمن مكونات أخرى
</h3>

<p>
	تتمثَّل إحدى الفوائد الرئيسية لبنية <a href="https://academy.hsoub.com/design/user-interface/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-ui-r652/" rel="">واجهة المستخدِم</a> القائمة على المكوّنات في أنه يمكن تكوين المكوّنات مع بعضها بعضًا، إذ يمكنك استخدام مكونات ضمن مكونات أخرى لبناء تطبيق ويب مثل كتابة وسوم HTML ضمن بعضها بعضًا لإنشاء موقع ويب، كما يتيح لك كل إطار عمل بكتابة مكوّنات تستخدِم وتعتمد على مكوّنات أخرى، كما يمكن استخدام مكوِّن React الذي هو <code>AuthorCredit</code> ضمن المكوِّن <code>Article</code> مثلًا، وهذا يعني حاجة المكوِّن <code>Article</code> إلى استيراد المكوِّن <code>AuthorCredit</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8337_45" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">AuthorCredit</span><span class="pln"> from </span><span class="str">"./components/AuthorCredit"</span><span class="pun">;</span></pre>

<p>
	يمكن بعد ذلك استخدام المكوِّن <code>AuthorCredit</code> ضمن المكوِّن <code>Article</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8337_47" style="">
<span class="pun">...</span><span class="pln">

</span><span class="pun">&lt;</span><span class="typ">AuthorCredit</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">

  </span><span class="pun">…</span></pre>

<h3>
	حقن الاعتماديات
</h3>

<p>
	تشتمل التطبيقات الواقعية على بنى مكونات ذات مستويات متعددة من التداخل Nesting في أغلب الأحيان، وقد يحتاج مكوِّن <code>AuthorCredit</code> المتداخل بعمق في العديد من المستويات لسبب ما إلى بيانات من المستوى الجذر لتطبيقنا، ولنفترض تنظيم موقع المجلة الذي نبنيه على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8337_49" style="">
<span class="pun">&lt;</span><span class="typ">App</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">Home</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">Article</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">AuthorCredit</span><span class="pln"> </span><span class="pun">{</span><span class="com">/* props */</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">Article</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="typ">Home</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="typ">App</span><span class="pun">&gt;</span></pre>

<p>
	يحتوي المكوِّن <code>App</code> على البيانات التي يحتاجها المكوِّن <code>AuthorCredit</code>، كما يمكننا إعادة كتابة المكوِّن <code>Home</code> و<code>Article</code> لمعرفة كيفية تمرير الخاصيات، ولكن قد يكون ذلك مملًا إذا كان هناك العديد من المستويات بين أصل ووجهة البيانات، كما قد يؤثر ذلك على الأداء، فلا يستخدِم المكوِّنان <code>Home</code> و<code>Article</code> صورة المؤلف أو السطر القصير الذي يعطي معلومات مختصَرةً عن المؤلف، ولكن إذا أردنا الحصول على هذه المعلومات في المكوِّن <code>AuthorCredit</code>، فينحتاج إلى تغيير المكوِّنَين <code>Home</code> و<code>Article</code> لإضافتها.
</p>

<p>
	تُسمَّى مشكلة تمرير البيانات عبر العديد من طبقات المكوّنات بتمرير الخاصيات Prop Drilling التي لا تُعَدّ مثاليةً للتطبيقات الكبيرة، إذ يمكن التحايل على هذه المشكلة من خلال توفير أطر العمل وظيفة تُعرَف باسم حقن الاعتمادية Dependency Injection، وهي طريقة لإعطاء بيانات معينة مباشرةً إلى المكوّنات التي تحتاجها دون تمريرها عبر المستويات المتداخلة، إذ ينفّذ كل إطار عمل عملية حقن الاعتمادية تحت اسم مختلف وبطريقة مختلفة، ولكن التأثير هو نفسه في النهاية.
</p>

<p>
	يسمّي إطار العمل Angular هذه العملية حقن الاعتمادية، في حين يمتلك إطار العمل Vue توابع المكوّنات <code>provide()‎</code> و<code>inject()‎</code>؛ أما React، فيحتوي على واجهة برمجة تطبيقات السياق Context <abbr title="Application Programming Interface | واجهة برمجية">API</abbr>، بينما يشارك إطار عمل Ember الحالة من خلال خدمات.
</p>

<h3>
	دورة الحياة Life Cycle
</h3>

<p>
	تُعَدّ دورة حياة المكوّن في سياق إطار العمل مجموعةً من المراحل التي يمر بها المكوِّن من وقت إلحاقه بنموذج DOM ثم تصييره بواسطة المتصفح -والذي يدعى بالتركيب Mounting في أغلب الأحيان- إلى وقت إزالته من نموذج DOM -والذي يطلَق عليه التفكيك Unmounting في أغلب الأحيان، كما يسمّي كل إطار عمل مراحل دورة الحياة هذه بطريقة مختلفة، ولا تمنح جميعها المطورين الوصول إلى المراحل نفسها، كما تتبع جميع الأطر النموذج العام نفسه، إذ تسمح للمطورين بتنفيذ إجراءات معينة عند تركيب Mount المكوِّن، وعند تصييره، وعند تفكيكه Unmount، وفي عدة مراحل بينها.
</p>

<p>
	تُعَدّ مرحلة التصيير Render المرحلة الأهم، لأنها تتكرر عندما يتفاعل المستخدِم مع تطبيقك، وتُشغَّل في كل مرة يحتاج فيها المتصفح إلى تصيير شيء جديد، سواءً كانت هذه المعلومات الجديدة إضافةً إلى ما هو موجود في المتصفح أو حذفه أو تعديله، كما يمكنك الاطلاع على <a href="https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/" rel="external nofollow">هذا الرسم البياني لدورة حياة مكون React</a> الذي يوضِّح هذا المفهوم.
</p>

<h2>
	تصيير العناصر
</h2>

<p>
	تتخِذ أطر العمل أساليبًا مختلفةً ولكنها متشابهة لتصيير التطبيقات، وتتعقّب جميعها الإصدار الحالي المُصيَّر من DOM في متصفحك، كما تتخذ كل منها قرارات مختلفةً قليلًا حول كيفية تغيير نموذج DOM مثل إعادة تصيير المكوّنات في تطبيقك، وبما أنّ أطر العمل تتخِذ هذه القرارات نيابةً عنك، فهذا يعني أنك لا تتفاعل مع نموذج DOM بنفسك، كما يُعَدّ هذا التجريد البعيد عن نموذج DOM أكثر تعقيدًا واستهلاكًا للذاكرة من تحديثه بنفسك، ولكن لا يمكن لأطر العمل بدونه السماح لك بالبرمجة بالطريقة التصريحية المعروفة بها.
</p>

<p>
	يُعَدّ نموذج DOM الافتراضي Virtual DOM نهجًا يمكن من خلاله تخزين معلومات حول نموذج DOM في متصفحك ضمن ذاكرة جافاسكربت، إذ يحدّث تطبيقك هذه النسخة من DOM، ثم يوازنها مع DOM الحقيقي المصيَّر لمستخدِميك فعليًا لتحديد ما سيُصيَّر، كما ينشئ التطبيق اختلافًا Diff لموازنة الاختلافات بين DOM الافتراضي المُحدَّث و DOM المُصيَّر حاليًا، إذ يُستخدَم هذا الاختلاف لتطبيق التحديثات على نموذج DOM الحقيقي، كما يستخدِم كل من React وVue نموذج DOM الافتراضي، لكنهما لا يطبِّقان المنطق نفسه بالضبط عند تطبيق الاختلاف Diffing أو التصيير Rendering، ويمكنك قراءة المزيد عن <a href="https://wiki.hsoub.com/React/faq_internals" rel="external">DOM الافتراضي</a> في توثيق React على موسوعة حسوب.
</p>

<p>
	يشبه نموذج DOM التزايدي Incremental DOM نموذج DOM الافتراضي Virtual DOM في أنه ينشئ اختلافًا في نموذج DOM لتحديد ما سيُصيَّر، إلا أنه يختلف في عدم إنشائه نسخةً كاملةً من DOM في ذاكرة جافاسكربت، وهو يتجاهل أجزاء DOM التي لا تحتاج إلى تغيير، فإطار العمل Angular هو الإطار الوحيد الذي ناقشناه حتى الآن والذي يستخدِم نموذج DOM التزايدي، كما يمكنك قراءة المزيد حول <a href="https://auth0.com/blog/incremental-dom/" rel="external nofollow">نموذج DOM التزايدي</a> على مدونة Auth0.
</p>

<p>
	آلة Glimmer الافتراضية خاصة بإطار عمل Ember، ولا تُعَدّ نموذج DOM افتراضي أو DOM تزايدي، وإنما هي عملية منفصلة يمكن من خلالها تحويل قوالب Ember إلى نوع من شيفرة ثنائية Byte Code تكون أسهل وأسرع في القراءة من <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">جافاسكربت</a>.
</p>

<h2>
	التوجيه Routing
</h2>

<p>
	يُعَدّ التوجيه جزءًا مهمًا من تجربة الويب، إذ يوفِّر كل إطار عمل مكتبةً أو أكثر بحيث تساعد المطورين على تنفيذ التوجيه من جانب العميل في تطبيقاتهم، لتجنّب تجربة معطَّلة في التطبيقات المعقدة ذات المشاهدات الكثيرة.
</p>

<h2>
	الاختبار Testing
</h2>

<p>
	تستفيد جميع التطبيقات من تغطية الاختبار التي تضمن استمرار برنامجك في التصرف بالطريقة التي تتوقعها، إذ يوفر النظام المجتمعي لكل إطار عمل الأدوات التي تسهّل كتابة الاختبارات، ولا تُضمَّن أدوات الاختبار في الأطر نفسها، ولكن تمنحك أدوات واجهة سطر الأوامر المُستخدَمة لإنشاء تطبيقات إطار العمل، الوصول إلى أدوات الاختبار المناسبة، إذ يحتوي كل إطار على أدوات واسعة النطاق في نظامه المجتمعي مع إمكانات اختبار الوحدة والتكامل على حد سواء.
</p>

<p>
	تُعَدّ <a href="https://testing-library.com/" rel="external nofollow">مكتبة الاختبار Testing Library</a> مجموعةً من أدوات الاختبار المساعِدة التي تحتوي على أدوات للعديد من بيئات جافاسكربت بما في ذلك React وVue وAngular، ويغطي توثيق Ember <a href="https://guides.emberjs.com/release/testing/" rel="external nofollow">اختبار تطبيقاته</a>، وإليك اختبار سريع للمكوِن <code>CounterButton</code> مكتوب بمساعَدة مكتبة اختبار React، إذ يختبر هذا الاختبار عددًا من الأشياء مثل وجود الزر وما إذا كان الزر يعرض النص الصحيح بعد النقر عليه 0 و1 و2 مرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8337_10" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">"react"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> render</span><span class="pun">,</span><span class="pln"> fireEvent </span><span class="pun">}</span><span class="pln"> from </span><span class="str">"@testing-library/react"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="str">"@testing-library/jest-dom/extend-expect"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">CounterButton</span><span class="pln"> from </span><span class="str">"./CounterButton"</span><span class="pun">;</span><span class="pln">

it</span><span class="pun">(</span><span class="str">"renders a semantic with an initial state of 0"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> getByRole </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> render</span><span class="pun">(&lt;</span><span class="typ">CounterButton</span><span class="pln"> </span><span class="pun">/&gt;);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> btn </span><span class="pun">=</span><span class="pln"> getByRole</span><span class="pun">(</span><span class="str">"button"</span><span class="pun">);</span><span class="pln">

  expect</span><span class="pun">(</span><span class="pln">btn</span><span class="pun">).</span><span class="pln">toBeInTheDocument</span><span class="pun">();</span><span class="pln">
  expect</span><span class="pun">(</span><span class="pln">btn</span><span class="pun">).</span><span class="pln">toHaveTextContent</span><span class="pun">(</span><span class="str">"Clicked 0 times"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

it</span><span class="pun">(</span><span class="str">"Increments the count when clicked"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> getByRole </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> render</span><span class="pun">(&lt;</span><span class="typ">CounterButton</span><span class="pln"> </span><span class="pun">/&gt;);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> btn </span><span class="pun">=</span><span class="pln"> getByRole</span><span class="pun">(</span><span class="str">"button"</span><span class="pun">);</span><span class="pln">

  fireEvent</span><span class="pun">.</span><span class="pln">click</span><span class="pun">(</span><span class="pln">btn</span><span class="pun">);</span><span class="pln">
  expect</span><span class="pun">(</span><span class="pln">btn</span><span class="pun">).</span><span class="pln">toHaveTextContent</span><span class="pun">(</span><span class="str">"Clicked 1 times"</span><span class="pun">);</span><span class="pln">

  fireEvent</span><span class="pun">.</span><span class="pln">click</span><span class="pun">(</span><span class="pln">btn</span><span class="pun">);</span><span class="pln">
  expect</span><span class="pun">(</span><span class="pln">btn</span><span class="pun">).</span><span class="pln">toHaveTextContent</span><span class="pun">(</span><span class="str">"Clicked 2 times"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<h2>
	الخلاصة
</h2>

<p>
	يجب أن يكون لديك الآن مزيدًا من الأفكار حول اللغات والميزات والأدوات الفعلية التي ستستخدِمها أثناء إنشاء التطبيقات باستخدام أطر العمل، ولا بدّ أنك متحمس للبدء بكتابة الشيفرة، وهذا ما ستفعله لاحقًا، ولكن يمكنك الآن اختيار إطار العمل الذي ترغب في بدء تعلمه أولًا مثل:
</p>

<ul>
<li>
		React
	</li>
	<li>
		Ember
	</li>
	<li>
		Vue
	</li>
	<li>
		Svelte
	</li>
	<li>
		Angular
	</li>
</ul>
<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Main_features" rel="external nofollow">Framework main features</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A3%D8%B7%D8%B1-%D8%B9%D9%85%D9%84-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1567/" rel="">مقدمة إلى أطر عمل تطوير الويب من طرف العميل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D9%81%D9%87%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1462/" rel="">فهم أدوات تطوير الويب من طرف العميل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%A8%D9%86%D8%A7%D8%A1-%D9%86%D9%85%D9%88%D8%B0%D8%AC-%D9%83%D8%A7%D9%85%D9%84-%D9%84%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1473/" rel="">بناء نموذج كامل لسلسلة أدوات تطوير الويب من طرف العميل</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1568</guid><pubDate>Fri, 10 Jun 2022 15:05:01 +0000</pubDate></item><item><title>&#x645;&#x627; &#x647;&#x64A; &#x62A;&#x642;&#x646;&#x64A;&#x629; Socket.io</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D8%A7-%D9%87%D9%8A-%D8%AA%D9%82%D9%86%D9%8A%D8%A9-socketio-r1782/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_11/636c89891eaef_socketio(1).png.d35a43e5c06a164ec1be47482151f655.png" /></p>

<p>
	إن كنت مهتمًا بمجال الويب فلا بد أنك قد تكون سمعت عن مكتبة socket.io، وهي مكتبة جافاسكربت. في هذا الفيديو، سنشرح مكتبة socket.io ونلخص أبرز المزايا التي تقدمها لنا وكيفية استخدامها من خلال الأمثلة العملية في socket.io.
</p>

<p style="text-align: center;">
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="480" title="ما هي تقنية Socket.io" width="853" src="https://www.youtube.com/embed/mqGHXndePeM"></iframe>
</p>

<p>
	يمكنك تعلم التقنيات الحديثة في جافاسكربت مجانًا عبر <a href="https://academy.hsoub.com/programming/" rel="">قسم البرمجة في أكاديمية حسوب</a>، كما يمكنك التعرف أكثر باحترافية أكبر على تقنية socket.io ومختلف التقنيات الحديثة من خلال الانضمام إلى <a href="https://academy.hsoub.com/learn/javascript-application-development/" rel="">دورة JavaScript</a> المقدمة من أكاديمية حسوب، ولا تنسى دعم رحلة تعلمك وعملك <a href="https://wiki.hsoub.com/JavaScript" rel="external">بتوثيقات موسوعة حسوب لجافاسكربت.</a>
</p>
]]></description><guid isPermaLink="false">1782</guid><pubDate>Thu, 09 Jun 2022 15:00:00 +0000</pubDate></item><item><title>&#x645;&#x642;&#x62F;&#x645;&#x629; &#x625;&#x644;&#x649; &#x623;&#x637;&#x631; &#x639;&#x645;&#x644; &#x62A;&#x637;&#x648;&#x64A;&#x631; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x645;&#x646; &#x637;&#x631;&#x641; &#x627;&#x644;&#x639;&#x645;&#x64A;&#x644;</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A3%D8%B7%D8%B1-%D8%B9%D9%85%D9%84-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1567/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_05/62815e886bda7_--------.png.c499e9905a573fbc2e680e7584c75aed.png" /></p>
<p>
	سنبدأ مقالنا بإلقاء نظرة على تاريخ <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-javascript-r664/" rel="">لغة جافاسكربت JavaScript</a> وأطر العمل Frameworks، وسبب وجود هذه الأطر وفوائدها، وكيفية اختيار إطار عمل، وما هي البدائل المتاحة لأطر العمل من طرف العميل.
</p>

<ul>
	<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و <a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و <a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت</a>.
	</li>
	<li>
		<strong>الهدف</strong>: فهم أطر عمل جافاسكربت من طرف العميل والمشاكل التي تحلها وبدائلها وكيفية اختيارها.
	</li>
</ul>

<h2>
	لمحة تاريخية
</h2>

<p>
	ظهرت لغة جافاسكربت لأول مرة في عام 1996 والتي أضافت تفاعلًا إلى الويب الذي احتوى على مستندات ثابتة سابقًا، وبالتالي أصبح الويب مكانًا لفعل أشياء، وليس مجرد مكان لقراءة تلك الأشياء، كما زادت شعبية جافاسكربت، كما كتب المطورون الذين عملوا معها، أدوات لحل المشاكل التي واجهوها، وجمّعوها في حزم قابلة لإعادة الاستخدام سميّت بمكتبات Libraries ليتمكنوا من مشاركة حلولهم مع بعضهم البعض، إذ ساعد هذا النظام المجتمعي المشترك للمكتبات في تشكيل نمو الويب.
</p>

<p>
	تُعَدّ لغة جافاسكربت الآن جزءًا أساسيًا من الويب، وتُستخدَم في 95% من مواقع الويب، كما أصبح المستخدِمون يكتبون أوراقًا papers، ويديرون ميزانياتهم، ويستمعون إلى الموسيقى، ويشاهدون الأفلام، ويتواصلون مع بعضهم البعض عن بعد فوريًا من خلال الدردشة النصية أو الصوتية أو المرئية، وبالتالي أتاح الويب تنفيذ أمور كانت ممكنةً سابقًا فقط في التطبيقات الأصيلة المثبَّتة على حواسيبنا، إذ يُشار إلى هذه المواقع الحديثة والمعقَّدة والتفاعلية باسم تطبيقات الويب Web Applications.
</p>

<p>
	أدى ظهور أطر عمل جافاسكربت الحديثة إلى تسهيل إنشاء تطبيقات تفاعلية وديناميكية، فإطار العمل Framework هو عبارة عن مكتبة تقدّم آراءً لبناء البرمجيات، إذ تسمح هذه الآراء بإمكانية التنبؤ والتجانس في التطبيق، فالقدرة على التنبؤ تسمح للبرمجيات بالتوسع إلى حجم هائل مع بقائها قابلةً للصيانة، إذ تُعَدّ القدرة على التنبؤ وقابلية الصيانة أمران ضروريان لصحة البرمجيات وطول عمرها.
</p>

<p>
	تعمل أطر عمل جافاسكربت على تشغيل الكثير من البرمجيات الرائعة على الويب الحديث، بما في ذلك العديد من مواقع الويب التي يُحتمَل استخدامها كل يوم، إذ تستخدِم صفحة توثيق الويب في MDN مثلًا إطار عمل React/ReactDOM لتشغيل واجهتها الأمامية، وتوجد هناك أطر عمل متعددة، ولكن "الأربعة الكبار" هي:
</p>

<h3>
	إمبر Ember
</h3>

<p>
	أُصدِر إطار عمل <a href="https://emberjs.com/" rel="external nofollow">Ember</a> في ديسمبر كانون الأول عام 2011 على أساس استمرار للعمل الذي بدأ في مشروع SproutCore، ويُعَدّ Ember إطار عمل قديم به عدد مستخدِمين أقل من البدائل الحديثة مثل React وVue، لكنه لا يزال يتمتع بقدر لا بأس به من الشعبية نظرًا لاستقراره ودعم المجتمع وبعض مبادئ البرمجة الذكية.
</p>

<h3>
	Angular
</h3>

<p>
	هو إطار عمل لتطبيق ويب مفتوح المصدر بقيادة فريق أنجولار <a href="https://angular.io" rel="external nofollow">Angular</a> في جوجل ومجتمع من الأفراد والشركات، إذ نتج <a href="https://academy.hsoub.com/programming/javascript/angular/" rel="">Angular</a> عن إعادة كتابة AngularJS بالكامل من الفريق نفسه الذي بناه وقد أُصدِر أنجولار رسميًا في 14 سبتمبر أيلول من عام 2016، وهي إطار عمل قائم على المكونات، وتستخدِم قوالب HTML التصريحية Declarative، كما يترجم مصرّف إطار العمل القوالب إلى تعليمات جافاسكربت محسَّنة في وقت البناء وبشفافية عن المطورين، تستخدِم أنجولار لغة <a href="https://wiki.hsoub.com/TypeScript" rel="external">TypeScript</a>، وهي مجموعة شاملة من لغة جافاسكربت التي سنلقي نظرةً عليها بمزيد من التفصيل لاحقًا.
</p>

<h3>
	Vue
</h3>

<p>
	أصدَر إيفان يو Evan You لأول مرة إطار عمل <a href="https://academy.hsoub.com/programming/javascript/vuejs/" rel="">Vue</a> في عام 2014 بعد العمل والتعلم من مشروع <a href="https://angularjs.org/" rel="external nofollow">AngularJS</a> الأصلي، إذ يُعَدّ Vue الأصغر بين الأربعة الكبار، لكنه تمتَّع مؤخرًا بشعبية متزايدة، كما يوسّع إطار عمل <a href="https://vuejs.org" rel="external nofollow">Vue</a> مثل AngularJS لغة HTML بشيفرته، وهو يعتمد بصورة أساسية على لغة جافاسكربت المعيارية الحديثة.
</p>

<h3>
	React
</h3>

<p>
	أصدَرت شركة فيسبوك مكتبة <a href="https://wiki.hsoub.com/React" rel="external">React</a> في عام 2013، إذ استخدِمت <a href="https://reactjs.org" rel="external nofollow">React</a> قبل ذلك في حل العديد من مشاكلها داخليًا، ولا تُعَدّ React نفسها إطار عمل، وإنما مكتبةً لتصيير Rendering مكونات واجهة المستخدِم، كما تُستخدَم React جنبًا إلى جنب مع المكتبات الأخرى لإنشاء التطبيقات، إذ تُمكِّن React و<a href="https://wiki.hsoub.com/ReactNative" rel="external">React Native</a> المطورين من إنشاء تطبيقات للهاتف المحمول، في حين تمكِّن React و<a href="https://reactjs.org/docs/react-dom.html" rel="external nofollow">ReactDOM</a> المطورين من إنشاء تطبيقات الويب، وتُعرَف React بوصفها إطار عمل جافاسكربت نظرًا لاستخدام React وReactDOM معًا في كثير من الأحيان، كما توسّع React لغة جافاسكربت بصيغة تشبه لغة HTML، إذ تُعرَف هذه الصيغة باسم <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B5%D9%8A%D8%BA%D8%A9-javascript-syntax-extension-jsx-r777/" rel="">JSX</a>.
</p>

<h2>
	سبب وجود أطر العمل
</h2>

<p>
	ناقشنا البيئة التي ألهمت إنشاء أطر العمل، ولكننا لم نناقش السبب الحقيقي لحاجة المطورين إلى إنشائها، إذ يتطلب استكشاف الأسباب فحص تحديات تطوير البرمجيات أولًا.
</p>

<p>
	ضع في الحسبان نوعًا شائعًا من التطبيقات، وهو مُنشئ قائمة المهام To-do List Creator التي سننفِّذها باستخدام مجموعة متنوعة من أطر العمل لاحقًا، إذ يجب أن يسمح هذا التطبيق للمستخدِمين بتطبيق بعض الأمور مثل عرض قائمة المهام وإضافة مهمة جديدة وحذف مهمة، كما يجب أن ينفّذ ذلك أثناء تتبّع وتحديث بيانات التطبيق الأساسية بصورة موثوقة، إذ تُعرَف هذه البيانات الأساسية بالحالة State في تطوير البرمجيات.
</p>

<p>
	هذه الأهداف بسيطة من الناحية النظرية بمعزل عن أهداف الأخرى، إذ يمكننا تكرار البيانات لتصييرها، كما يمكننا إضافة كائن لعمل مهمة جديدة، واستخدام معرِّف Identifier للعثور على مهمة أو تعديلها أو حذفها، إذ يجب أن يسمح التطبيق للمستخدِم بتطبيق كل هذه الأشياء من خلال المتصفح، ولكن قد تبدأ بعض المشاكل في الظهور، إلا أنّ المشكلة الحقيقية هي الحاجة إلى تحديث واجهة المستخدِم المناسبة في كل مرة نغيّر فيها حالة تطبيقنا، إذ يمكننا فحص صعوبة هذه المشكلة من خلال النظر إلى ميزة واحدة فقط من تطبيق قائمة مهام وهي عرض قائمة المهام.
</p>

<h2>
	تغيرات DOM المطولة
</h2>

<p>
	يستغرق إنشاء عناصر HTML وتصييرها في المتصفح في الوقت المناسب قدرًا كبيرًا من الشيفرة، ولنفترض أنّ حالتنا هي مصفوفة من الكائنات كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1919_8" style=""><span class="pln">const state = [
  {
    id: 'todo-0',
    name: 'Learn some frameworks!'
  }
]</span></pre>

<p>
	يمكنك التساؤل عن كيفية عرض إحدى هذه المهام للمستخدِم، إذ نريد تمثيل كل مهمة بعنصر قائمة، أي العنصر <code>&lt;li&gt;</code> في لغة HTML ضمن عنصر القائمة غير المرتبة <code>&lt;ul&gt;</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1919_10" style=""><span class="pln">function buildTodoItemEl(id, name) {
  const item = document.createElement('li');
  const span = document.createElement('span');
  const textContent = document.createTextNode(name);

  span.appendChild(textContent);

  item.id = id;
  item.appendChild(span);
  item.appendChild(buildDeleteButtonEl(id));

  return item;
}</span></pre>

<p>
	استخدمنا التابع <code>document.createElement()‎</code> لإنشاء العنصر <code>&lt;li&gt;</code> والعديد من أسطر الشيفرة لإنشاء الخصائص والعناصر الأبناء التي يحتاجها، في حين يشير جزء الشيفرة التالي إلى دالة بناء أخرى هي <code>buildDeleteButtonEl()‎</code>، والتي تتبع نمطًا مشابهًا للنمط الذي استخدمناه لبناء عنصر القائمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1919_16" style=""><span class="kwd">function</span><span class="pln"> buildDeleteButtonEl</span><span class="pun">(</span><span class="pln">id</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> button </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'button'</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> textContent </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createTextNode</span><span class="pun">(</span><span class="str">'Delete'</span><span class="pun">);</span><span class="pln">

  button</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">'type'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'button'</span><span class="pun">);</span><span class="pln">
  button</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">textContent</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> button</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لا ينفِّذ هذا الزر أيّ شيء حتى الآن، ولكنه سينفِّذ شيئًا ما لاحقًا عندما نقرِّر تنفيذ ميزة الحذف، كما يمكن أن تقرأ الشيفرة التي ستصيّر العناصر على الصفحة شيئًا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1919_18" style=""><span class="kwd">function</span><span class="pln"> renderTodoList</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> frag </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createDocumentFragment</span><span class="pun">();</span><span class="pln">
  state</span><span class="pun">.</span><span class="pln">tasks</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">task </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> item </span><span class="pun">=</span><span class="pln"> buildTodoItemEl</span><span class="pun">(</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">,</span><span class="pln"> task</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
    frag</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">item</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">todoListEl</span><span class="pun">.</span><span class="pln">firstChild</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    todoListEl</span><span class="pun">.</span><span class="pln">removeChild</span><span class="pun">(</span><span class="pln">todoListEl</span><span class="pun">.</span><span class="pln">firstChild</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  todoListEl</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">frag</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لدينا الآن أكثر من ثلاثين سطرًا من الشيفرة المخصَّصة لواجهة المستخدِم فقط -أي إلى خطوة تصيير شيء ما في <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">DOM</a>- دون إضافة أصناف Classes التي يمكننا استخدامها لاحقًا لتصميم عناصر القائمة، كما يتطلب العمل مباشرةً مع نموذج DOM فهم أشياء كثيرة حول كيفية عمله مثل كيفية إنشاء العناصر، وتغيير خصائصها، وكيفية وضع العناصر ضمن بعضها البعض، والحصول عليها على الصفحة، فلا تعالج هذه الشيفرة تفاعلات المستخدِم أو إضافة مهمة أو حذفها، فإذا أضفنا هذه الميزات، فيجب علينا تذكّر تحديث واجهة المستخدِم في الوقت المناسب وبالطريقة الصحيحة.
</p>

<p>
	أُنشِئت أطر عمل جافاسكربت لتسهيل هذا النوع من العمل، إذ أُوجِدت لتوفير تجربة مطوِّر أفضل، فهي لا تضيف ميزات جديدة إلى جافاسكربت، وإنما تمنحك وصولًا أسهل لميزاتها لتتمكّن من بناء تطبيقات ويب بطريقة عصرية، فإذا أردت رؤية نماذج شيفرة هذا المقال عمليًا، فيمكنك التحقق من إصدار عامل من التطبيق على <a href="https://codepen.io/mxmason/pen/XWbPNmw" rel="external nofollow">CodePen</a> الذي يسمح للمستخدِمين بإضافة مهام جديدة وحذفها.
</p>

<h2>
	طريقة أخرى لبناء واجهات المستخدم
</h2>

<p>
	توفّر إطارات عمل جافاسكربت طريقةً لكتابة واجهات المستخدِم بطريقة تصريحية، أي أنها تسمح بكتابة الشيفرة التي توضِّح كيف يجب أن تبدو واجهة المستخدِم، كما يحقّق إطار العمل ذلك ضمن نموذج DOM في الخلفية، وكان فهم منهج جافاسكربت الصرفة Vanilla JavaScript لإنشاء عناصر DOM جديدة بطريقة تكرارية في لمح البصر أمرًا صعبًا، لكن يوضِّح الجزء التالي من الشيفرة الطريقة التي يمكنك من خلالها استخدام إطار عمل Vue لوصف قائمة من المهام:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1919_22" style=""><span class="pun">&lt;</span><span class="pln">ul</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">li v</span><span class="pun">-</span><span class="kwd">for</span><span class="pun">=</span><span class="str">"task in tasks"</span><span class="pln"> v</span><span class="pun">-</span><span class="pln">bind</span><span class="pun">:</span><span class="pln">key</span><span class="pun">=</span><span class="str">"task.id"</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">span</span><span class="pun">&gt;{{</span><span class="pln">task</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}}&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pun">&gt;</span><span class="typ">Delete</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">li</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">ul</span><span class="pun">&gt;</span></pre>

<p>
	يختصر جزء الشيفرة السابق ما يقرب من اثنين وثلاثين سطرًا من الشيفرة في ستة أسطر، فإذا كانت الأقواس المعقوصة وسمات <code>v-‎</code> غير مألوفة لك، فلا بأس بذلك، إذ سنتعرّف على الصيغة التي يستخدِمها إطار عمل Vue لاحقًا، كما تشبه الشيفرة السابقة واجهة المستخدِم التي تمثِّلها، في حين لا تشبه شيفرة جافاسكربت الصرفة ذلك.
</p>

<p>
	يمكن عدم كتابة دوالنا لبناء واجهة المستخدم بفضل إطار عمل Vue الذي سيتعامل مع ذلك بطريقة مثلى وفعالة، فدورنا الوحيد هو شرحنا لإطار عمل Vue الشكل الذي يجب أن يبدو عليه كل عنصر، كما يمكن للمطورين الذين هم على دراية بإطار عمل Vue الانضمام إلى مشروعنا والعمل بسرعة على ما يجري، فاستعمال إطار العمل Vue -وأي إطار عمل آخر- يحسّن كفاءة الفريق وأعضائه.
</p>

<p>
	يمكن تطبيق أشياء مشابهة في لغة جافاسكربت الصرفة، إذ تسهّل سلاسل القالب الحرفية Template literal strings كتابة سلاسل HTML التي تمثِّل الصورة التي سيبدو عليها العنصر الأخير، وقد يكون ذلك فكرةً مفيدةً لشيء بسيط مثل تطبيق قائمة المهام، ولكنه ليس قابلًا للصيانة للتطبيقات الكبيرة التي تتعامل مع آلاف سجلات البيانات، كما يمكن تصيير العديد من العناصر الفريدة في واجهة المستخدِم.
</p>

<h2>
	فوائد أطر العمل الأخرى
</h2>

<p>
	لنلقِ نظرةً على بعض المزايا الأخرى التي تمنحنا إياها أطر العمل، كما يمكن تحقيق مزايا الأطر في لغة جافاسكربت الصرفة، ولكن يزيح استخدام إطار العمل عبء ضرورة حل هذه المشاكل بأنفسنا.
</p>

<h3>
	الأدوات
</h3>

<p>
	يمتلك كل إطار من أطر العمل مجتمع مطورين كبير ونشط، لذلك يوفِّر النظام المجتمعي لكل إطار الأدوات التي تعمل على تحسين تجربة المطوِّر، إذ تسهّل هذه الأدوات إضافة أشياء مثل الاختبار للتأكد من عمل تطبيقك كما ينبغي، أو تدقيق الصياغة Linting للتأكد من خلو شيفرتك البرمجية من الأخطاء متجانسها من حيث الأسلوب.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix" data-gramm="false">
		<p>
			ملاحظة: إذا أردت معرفة مزيد من التفاصيل حول مفاهيم أدوات الويب، فاطّلع على مقال <a href="https://academy.hsoub.com/programming/workflow/%D9%81%D9%87%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1462/" rel="">فهم أدوات تطوير الويب من طرف العميل</a>.
		</p>
	</div>
</blockquote>

<h3>
	التجزئة Compartmentalization
</h3>

<p>
	تشجع معظم الأطر الرئيسية المطورين على تجريد الأجزاء المختلفة من واجهات المستخدِم إلى مكونات Components، إذ تُعَدّ هذه المكونات أجزاءً من شيفرة برمجية قابلة للصيانة وإعادة الاستخدام ويمكنها التواصل مع بعضها بعضًا، كما يمكن وضْع الشيفرة المتعلقة بمكوّن معيّن في ملف واحد أو ملفين محدَّدين، بحيث يعرف المطوِّر بالضبط إلى أين يذهب لإجراء تغييرات على هذا المكوّن، في حين سيتعيّن عليك في تطبيق مكتوب بلغة جافاسكربت الصرفة إنشاء مجموعة اصطلاحات لتحقيق ذلك بطريقة فعالة وقابلة للتوسيع، كما يمكن انتهاء المطاف بالعديد من مطوري جافاسكربت بنشر الشيفرة البرمجية المتعلقة بجزء واحد من <a href="https://academy.hsoub.com/design/user-interface/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-ui-%D9%88%D9%85%D8%AA%D8%AD%D9%83%D9%85%D8%A7%D8%AA%D9%87%D8%A7-r529/" rel="">واجهة المستخدِم</a> في جميع أنحاء الملف أو في ملف آخر تمامًا.
</p>

<h3>
	التوجيه Routing
</h3>

<p>
	يتيح الويب للمستخدمين التنقل من صفحة إلى أخرى، إذ يُعَدّ شبكةً من الوثائق المترابطة، فإذا ضغطتَ على رابط في موقع الويب هذا، فسيتصل متصفحك بخادم ما ويجلب محتوًى جديدًا لعرضه لك، وبالتالي سيتغير عنوان URL في شريط العنوان، ويمكنك حفظ <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87-r1435/" rel="">عنوان URL</a> الجديد والعودة إلى الصفحة لاحقًا، أو مشاركته مع الآخرين ليتمكنوا من العثور على الصفحة نفسها بسهولة، كما يتذكر متصفحك سجل التنقل ويسمح لك بالتنقل ذهابًا وإيابًا، وهذا ما يسمى بالتوجيه من طرف الخادم Server Side Routing.
</p>

<p>
	لا تجلب تطبيقات الويب الحديثة عادةً ملفات HTML الجديدة لتصييرها، وإنما تحمّل صفحة HTML واحدةً وتحدِّث نموذج DOM ضمنها باستمرار -ويشار إليها باسم تطبيقات الصفحة الواحدة Single Page Apps أو SPAs اختصارًا- دون انتقال المستخدِمين إلى عناوين جديدة على الويب، كما يطلَق عادةً على كل صفحة ويب وهمية Pseudo-Webpage جديدة اسم عرض View دون إجراء أيّ توجيه.
</p>

<p>
	إذا كان تطبيق SPA معقدًا بدرجة كافية ويصيِّر عددًا كافيًا من العروض الفريدة، فيجب إدخال وظائف التوجيه في تطبيقك، وقد اعتاد الناس على القدرة على الارتباط بصفحات معينة في التطبيقات، والانتقال ذهابًا وإيابًا في سجل التنقّل وما إلى ذلك، ولكن تتأثر تجربتهم عند تعطل ميزات الويب المعيارية هذه، فإذا تعامل تطبيق عميل مع التوجيه بهذه الطريقة، فإنه يسمى بالتوجيه من طرف العميل Client Side Routing، كما يمكن إنشاء موجّه باستخدام إمكانيات جافاسكربت والمتصفح الأصيلة، لكن الأطر الشائعة والمطوّرة بطريقة نشطة لها مكتبات مرافقة تجعل التوجيه جزءًا أسهل في عملية التطوير.
</p>

<h2>
	أمور يجب مراعاتها عند استخدام الأطر
</h2>

<p>
	يفضِّل مطور الويب الفعال استخدام أنسب الأدوات لكل عمل، كما تسهّل أطر عمل جافاسكربت تطوير التطبيقات الأمامية، لكنها ليست حلًا سحريًا لحل جميع المشاكل، إذ سنوضِّح فيما يلي الأمور التي يجب مراعاتها عند استخدام الأطر، وضَع في الحسبان أنك قد لا تحتاج إلى إطار عمل إطلاقًا.
</p>

<h3>
	معرفة كيفية استخدام الأداة
</h3>

<p>
	تستغرق الأطر وقتًا للتعلم تمامًا مثل لغة جافاسكربت الصرفة، لذلك تأكد من امتلاكك الوقت لتعلّم ما يكفي من ميزات إطار العمل قبل أن تقرر استخدامه لمشروع ما ليكون مفيدًا لك بدلًا من أن يعيقك، وتأكد من راحة زملائك في الفريق في التعامل معه أيضًا.
</p>

<h3>
	الهندسة الفائقة Overengineering
</h3>

<p>
	إذا كان مشروع <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8/" rel="">تطوير الويب</a> ملفًا شخصيًا يتكون من بضع صفحات، وكانت هذه الصفحات ذات قدرة تفاعلية قليلة أو معدومة، فقد لا يكون إطار العمل وجافاسكربت ضروريين إطلاقًا، إذ لا تُعَدّ أطر العمل وحدةً مترابطةً، فبعضها أكثر ملاءمةً للمشاريع الصغيرة من غيرها، إذ كتبت سارة دراسنر Sarah Drasner في مقال لمجلة Smashing Magazine عن كيفية <a href="https://www.smashingmagazine.com/2018/02/jquery-vue-javascript/" rel="external nofollow">استبدال Vue بـ jQuery</a> بوصفها أداةً لجعل أجزاء صغيرة من صفحة ويب تفاعلية.
</p>

<h3>
	قاعدة شيفرة أكبر وتجريد أكبر
</h3>

<p>
	تسمح لك أطر العمل بكتابة المزيد من الشيفرة البرمجية التصريحية -وأحيانًا مقدار أقل من الشيفرة البرمجية- من خلال التعامل مع تفاعلات DOM نيابةً عنك في الخلفية، ويُعَدّ هذا التجريد رائعًا لتجربتك بوصفك مطورًا، ولكنه ليس مجانيًا، إذ يجب على أطر العمل تشغيل شيفرتها البرمجية لترجمة ما تكتبه إلى تغييرات DOM، والتي بدورها تجعل الجزء الأخير من البرنامج أكبر وأكثر تكلفةً.
</p>

<p>
	تُعَدّ الشيفرة البرمجية الإضافية أمرًا لا مفر منه، وسيسمح لك إطار العمل الذي يدعم تقنية هز الشجرة Tree-Shaking -أي إزالة أي شيفرة غير مُستخدَمة فعليًا في التطبيق أثناء عملية البناء- بالحفاظ على تطبيقاتك صغيرة، ولكن ذلك لا يزال عاملًا يجب وضعه في الحسبان عند التفكير في أداء تطبيقك، خاصةً على الأجهزة المقيَّدة بالشبكة أو بالتخزين مثل الهواتف المحمولة.
</p>

<p>
	لا يؤثِّر تجريد الأطر على شيفرة جافاسكربت فحسب، وإنما يؤثِّر على علاقتك بطبيعة الويب ذاتها، فنتيجة تطبيقك النهائية أو الطبقة التي يتفاعل معها المستخدِمون في النهاية هي HTML بغض النظر عن كيفية بنائك لتطبيق الويب، إذ يمكن أن تجعلك كتابة تطبيقك بالكامل باستخدام لغة جافاسكربت غافلًا عن HTML والغرض من وسومها المختلفة، وتقودك إلى إنتاج مستند HTML غير دلالي Un-semantic ولا يمكن الوصول إليه، إذ يمكن كتابة تطبيق هش يعتمد كليًا على جافاسكربت ولن يعمل بدونه.
</p>

<p>
	ليست الأطر مصدر مشاكلنا، إذ يمكن أن يكون أيّ تطبيق هشًا ومتضخمًا ولا يمكن الوصول إليه مع وجود أولويات خاطئة، ولكن يُعَدّ تضخم أطر العمل من أولويات المطورين، فإذا كانت أولوياتك هي إنشاء تطبيق ويب معقَّد، فيمكنك تنفيذ ذلك بسهولة، في حين إذا كانت أولوياتك هي عدم حماية الأداء وإمكانية الوصول، فستزيد أطر العمل من هشاشة تطبيقك وتضخمه وعدم إمكانية الوصول إليه، وقد أدّت أولويات المطور الحديثة التي ضخَّمتها أطر العمل، إلى قلب بنية الويب في مواضع مختلفة، إذ يضع الويب الآن جافاسكربت أولًا وتجربة المستخدِم أخيرًا في أغلب الأحيان بدلًا من إنشاء شبكة مستندات قوية وتعتمد على المحتوى.
</p>

<h2>
	إمكانية الوصول على شبكة ويب مقادة بأطر العمل
</h2>

<p>
	تتطلب إمكانية الوصول إلى واجهات المستخدِم بعض التفكير والجهد دائمًا، ويمكن أن تؤدي الأطر إلى تعقيد هذه العملية، إذ يجب استخدام واجهات برمجة تطبيقات إطار عمل متقدمة في أغلب الأحيان للوصول إلى ميزات المتصفح الأصيلة مثل مناطق <a href="https://academy.hsoub.com/programming/html/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%85%D9%88%D8%A7%D8%B5%D9%81%D8%A7%D8%AA-aria-%D8%A5%D8%B9%D8%B7%D8%A7%D8%A1-%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-html-%D8%AF%D9%84%D8%A7%D9%84%D8%A7%D8%AA-%D8%AE%D8%A7%D8%B5%D8%A9-%D9%84%D8%AA%D8%B3%D9%87%D9%8A%D9%84-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-r1327/" rel="">ARIA</a> الحية أو إدارة التركيز.
</p>

<p>
	تخلق تطبيقات إطار العمل في بعض الحالات حواجز وصول غير موجودة في المواقع التقليدية مثل التوجيه من طرف العميل كما ذكرنا سابقًا، في حين تكون للتنقل عبر الويب باستخدام التوجيه التقليدي من طرف الخادم نتائج يمكن التنبؤ بها، إذ يعرف المتصفح كيفية ضبط التركيز على الجزء العلوي من الصفحة وستعلن التقنيات المساعدة عن عنوان الصفحة، إذ تحدث هذه الأمور في كل مرة تنتقل فيها إلى صفحة جديدة.
</p>

<p>
	لا يحمّل متصفحك صفحات ويب جديدة باستخدام التوجيه من طرف العميل، لذلك لا يعرف أنه يجب عليه ضبط التركيز تلقائيًا أو الإعلان عن عنوان صفحة جديد، وقد كرّس مؤلفو أطر العمل وقتًا وعملًا هائلَين لكتابة شيفرة جافاسكربت التي تعيد إنشاء هذه الميزات، ولكن لم يطبّق أيّ إطار عمل ذلك بطريقة مثالية، وبذلك يجب عليك التفكير في إمكانية الوصول منذ بداية كل مشروع ويب، وضع في الحسبان أنه من المرجَّح معاناة قواعد الشيفرة المجردة التي تستخدِم الأطر من مشاكل الوصول الرئيسية إذا لم تفعل ذلك.
</p>

<h2>
	كيفية اختيار إطار العمل
</h2>

<p>
	يتّخذ كل إطار من أطر العمل التي ناقشناها سابقًا مناهج مختلفةً لتطوير تطبيقات الويب، إذ يتحسّن كل منها أو يتغير بانتظام، ولكل منها إيجابياته وسلبياته، كما يُعَدّ اختيار إطار العمل الصحيح عمليةً تعتمد على الفريق والمشروع، إذ يجب عليك إجراء بحث للكشف عمّا يناسب احتياجاتك، ولكننا حدّدنا بعض الأسئلة التي يمكنك طرحها من أجل البحث في خياراتك بفعالية أكبر، وهي كما يلي:
</p>

<ul>
	<li>
		ما المتصفحات التي يدعمها إطار العمل؟
	</li>
	<li>
		ما اللغات الخاصة بالنطاق التي يستخدمها إطار العمل؟
	</li>
	<li>
		هل يحتوي الإطار على مجتمع قوي وتوثيق جيد ودعم متاح؟
	</li>
</ul>

<p>
	يوفِّر الجدول الموجود في هذا المقال ملخصًا سريعًا لدعم المتصفح الحالي الذي يقدِّمه كل إطار عمل، بالإضافة إلى لغات المجال المحدَّد Domain-specific Languages -أو DSLs اختصارًا- التي يمكن استخدامها، إذ تُعَدّ لغات المجال المحدَّد لغات برمجة مرتبطةً بمجالات محدَّدة من تطوير البرمجيات، كما تُعَدّ في سياق أطر العمل أنها أنواع من لغات جافاسكربت أو HTML التي تسهّل التطوير باستخدام هذا الإطار.
</p>

<p>
	لا يتطلب أيّ إطار من أطر العمل مطوِّرًا لاستخدام لغة DSL معينة، ولكن صُمِّمت جميعها تقريبًا مع وضع لغة DSL محدَّدة في الحسبان، إذ يعني اختيار عدم استخدام لغة DSL المفضلة لإطار العمل أنك ستفقد الميزات التي من شأنها تحسين تجربة المطوِّر، كما يجب عليك التفكير بجدية في مصفوفة الدعم ولغات DSL الخاصة بإطار العمل عند اختيارك لأيّ مشروع جديد، إذ يمكن أن يكون دعم المتصفح غير المتطابق عائقًا أمام المستخدِمين، ويمكن أن يكون دعم لغة DSL غير المناسب عائقًا أمامك وأمام زملائك في الفريق.
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
	<thead>
		<tr>
			<th>
				إطار العمل
			</th>
			<th>
				دعم المتصفح
			</th>
			<th>
				لغة DSL المفضلة
			</th>
			<th>
				لغات DSL المدعومة
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				إطار العمل Angular
			</td>
			<td>
				المتصفح IE9+
			</td>
			<td>
				لغة TypeScript
			</td>
			<td>
				لغات HTML-based وTypeScript
			</td>
		</tr>
		<tr>
			<td>
				إطار العمل React
			</td>
			<td>
				متصفح IE9+ الحديث مع تعويض نقص دعم المتصفحات Polyfill
			</td>
			<td>
				صيغة JSX
			</td>
			<td>
				صيغة JSX ولغة TypeScript
			</td>
		</tr>
		<tr>
			<td>
				إطار العمل Vue
			</td>
			<td>
				المتصفح IE9+
			</td>
			<td>
				لغة HTML-based
			</td>
			<td>
				لغات HTML-based وJSX وPug
			</td>
		</tr>
		<tr>
			<td>
				إطار العمل Ember
			</td>
			<td>
				متصفح IE9+ الحديث في إصدار Ember رقم 2.18
			</td>
			<td>
				لغة Handlebars
			</td>
			<td>
				لغات Handlebars وTypeScript
			</td>
		</tr>
	</tbody>
</table>

<p>
	وفي ملاحظة مهمة، تجدر الإشارة إلى أن لغات DSL التي وصفناها بأنها " لغات تستند إلى HTML أو HTML-based" لا تمتلك أسماءً رسميةً، فهي ليست لغات DSL حقيقية، ولكنها لغة HTML غير معيارية، لذا وجب الإشارة إلى هذه النقطة.
</p>

<p>
	قد يكون مجتمع إطار العمل المقياس الأصعب في القياس، لأن حجم المجتمع لا يرتبط مباشرةً بالأعداد التي يسهل الوصول إليها، ويمكنك التحقق من عدد نجوم مشروع جيت هاب GitHub أو التنزيلات الأسبوعية من npm للحصول على فكرة عن شعبيته، ولكن أفضل ما يمكنك فعله في بعض الأحيان هو البحث في عدد قليل من المنتديات أو التحدث إلى مطوِّرين آخرين، إذ لا يتعلق الأمر بحجم المجتمع فحسب، وإنما يتعلق بشموليته ومدى جودة التوثيق المتاح.
</p>

<p>
	هناك مناقشات كثيرة في جميع أنحاء الويب حول إطار العمل الأفضل، فقد اختارت مؤسسة ويكيميديا Wikimedia مؤخرًا استخدام إطار العمل Vue لواجهتها الأمامية، ونشرت <a href="https://phabricator.wikimedia.org/T241180" rel="external nofollow">طلبًا للتعليقات Request For Comments</a> -أو RFC اختصارًا- حول اعتماد هذا الإطار، وقد استغرق إريك جاردنر Eric Gardner مؤلف RFC وقتًا لتوضيح احتياجات مشروع ويكيميديا وسبب كون بعض أطر العمل اختيارات جيدة للفريق، إذ يُعَدّ طلب التعليقات هذا مثالًا رائعًا لنوع البحث الذي يجب عليك تطبيقه بنفسك عند التخطيط لاستخدام إطار عمل للواجهة الأمامية.
</p>

<p>
	يُعَدّ <a href="https://stateofjs.com/" rel="external nofollow">استبيان حالة جافاسكربت</a> مجموعةً مفيدةً من ملاحظات مطوري جافاسكربت، كما يغطّي العديد من الموضوعات المتعلقة بجافاسكربت بما في ذلك البيانات حول استخدام أطر العمل ورأي المطورين بها، وهناك حاليًا مجموعة من البيانات المتاحة على مدى عدة سنوات، مما يسمح لك بالتعرف على شعبية إطار العمل، كما وازن فريق Vue بين <a href="https://vuejs.org/v2/guide/comparison.html" rel="external nofollow">Vue وأطر العمل الشائعة الأخرى</a>، إذ قد يكون هناك بعض التحيز في هذه الموازنة، لكنها تُعَدّ موردًا قيّمًا.
</p>

<h2>
	بدائل لأطر العمل من طرف العميل
</h2>

<p>
	إذا أردت البحث عن أدوات لتسريع عملية تطوير الويب، وعلمتَ أنّ مشروعك لن يتطلب شيفرة جافاسكربت مكثفةً من طرف العميل، فيمكنك الوصول إلى أحد الحلول القليلة لبناء الويب مثل:
</p>

<ul>
	<li>
		نظام إدارة المحتوى Content Management System.
	</li>
	<li>
		التصيير من طرف الخادم Server-side Rendering.
	</li>
	<li>
		مولّد موقع ساكن Static Site Generator.
	</li>
</ul>

<h3>
	أنظمة إدارة المحتوى
</h3>

<p>
	تُعَدّ أنظمة إدارة المحتوى Content Management Systems -أو CMSes اختصارًا- أدوات تسمح للمستخدِم بإنشاء محتوى للويب دون كتابة الشيفرة البرمجية مباشرةً، كما تُعَدً حلًا جيدًا للمشاريع الكبيرة وخاصةً المشاريع التي تتطلب مدخلات من كتّاب المحتوى الذين لديهم قدرةً محدودةً على كتابة شيفرة برمجية، أو للمبرمجين الذين يرغبون في توفير الوقت، إلا أنها تتطلب قدرًا كبيرًا من الوقت لإعدادها.
</p>

<p>
	يعني استخدام نظام CMS أنك تتخلى على الأقل عن قدر من التحكم في ناتج موقعك النهائي على الويب، فإذا لم يؤلِّف نظام إدارة المحتوى الذي اخترته محتوًى يمكن الوصول إليه افتراضيًا على سبيل المثال، فسيكون تحسين ذلك أمرًا صعبًا في أغلب الأحيان، وتشمل الأمثلة المستخدَمة حاليًا <a href="https://wordpress.com/" rel="external nofollow">ووردبريس Wordpress</a> و<a href="https://www.joomla.org/" rel="external nofollow">جوملا Joomla</a> و<a href="https://www.drupal.org/" rel="external nofollow">دروبال Drupal</a>.
</p>

<h3>
	التصيير من طرف الخادم
</h3>

<p>
	يُعَدّ التصيير من طرف الخادم Server-side Rendering -أو SSR اختصارًا- بنية تطبيقات تكون مهمة الخادم فيها تصيير تطبيق مؤلف من صفحة واحدة، وهو عكس التصيير من طرف العميل Client-side Rendering، كما يُعَدّ الطريقة الأكثر شيوعًا والأكثر مباشرةً لبناء تطبيق جافاسكربت، إذ يكون التصيير من طرف الخادم أسهل على جهاز العميل، لأنك ترسل ملف HTML المُصيَّر إليه فقط، ولكن يمكن أن يكون إعداده صعبًا بالموازنة مع التطبيق المُصيَّر من طرف العميل.
</p>

<p>
	تدعم جميع أطر العمل التي ذكرهانا في هذا المقال التصيير من طرف الخادم والتصيير من طرف العميل، كما يمكنك الاطلاع على <a href="https://nextjs.org/" rel="external nofollow">Next.js</a> لإطار العمل React و<a href="https://nuxtjs.org/" rel="external nofollow">Nuxt.js</a> لإطار العمل Vue و<a href="https://github.com/ember-fastboot/ember-cli-fastboot" rel="external nofollow">FastBoot</a> لإطار العمل Ember و<a href="https://angular.io/guide/universal" rel="external nofollow">Angular Universal</a> لإطار العمل Angular.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		ملاحظة: يكتب المجتمع بعض حلول التصيير SSR ويعمل على صيانتها، بينما تُعَدَ بعضها حلولًا رسميةً مقدمةً من مشرف إطار العمل.
	</p>
</blockquote>

<h3>
	مولدات الموقع الساكنة
</h3>

<p>
	تُعَدّ مولِّدات المواقع الساكنة Static Site Generators برامج تنشئ ديناميكيًا جميع صفحات الويب الخاصة بموقع متعدِّد الصفحات -بما في ذلك شيفرة جافاسكربت أو CSS ذات الصلة، بحيث يمكن نشرها في أماكن متعددة، كما يمكن أن يكون مضيف النشر فرعًا من صفحات جيت هاب أو مثيل Netlify أو أيّ خادم خاص من اختيارك مثلًا، ولهذا النهج مزايا متعددة فيما يتعلق بالأداء، فلا يبني جهاز المستخدِم الخاص بك الصفحة باستخدام جافاسكربت، فهو مكتمل فعليًا، والأمان، إذ تمتلك الصفحات الساكنة عددًا أقل من متجهات الهجوم، كما لا يزال بإمكان هذه المواقع استخدام شيفرة جافاسكربت حيثما يحتاجون إليها، لكنها لا تعتمد عليها، وتستغرق مولّدات المواقع الساكنة وقتًا لتعلّمها مثل أيّ أداة أخرى، كما يمكن أن تكون عائقًا أمام عملية التطوير.
</p>

<p>
	يمكن أن تحتوي المواقع الساكنة على صفحات فريدة قليلة أو كثيرة حسبما تريد، كما تمكّنك أطر العمل من كتابة تطبيقات جافاسكربت من طرف العميل بسرعة، وتتيح لك مولّدات المواقع الساكنة طريقةً لإنشاء ملفات HTML بسرعة، كما تسمح مولّدات المواقع الساكنة للمطورين بكتابة المكوّنات التي تحدِّد الأجزاء المشتركة من صفحات الويب الخاصة بك، وتكوين هذه المكونات معًا لإنشاء صفحة نهائية، إذ تسمَّى هذه المكونات ضمن سياق مولّدات الموقع الساكنة قوالبًا Templates، ويمكن أن تكون صفحات الويب التي أنشأها مولِّد المواقع الساكنة موطنًا لتطبيقات إطار العمل إذا أردت صفحةً واحدةً محدَّدةً من موقع الويب المُولَّد بطريقة ساكنة لتشغيل تطبيق React عندما يزوره المستخدِم مثلًا.
</p>

<p>
	مولّدات المواقع الساكنة موجودة منذ فترة طويلة، لكنها شهدت بعض التجدّد في تاريخ الويب الحديث، وتتوفر الآن منها مجموعة من الخيارات القوية مثل <a href="https://gohugo.io/" rel="external nofollow">Hugo</a> و<a href="https://jekyllrb.com/" rel="external nofollow">Jekyll</a> و<a href="https://www.11ty.dev" rel="external nofollow">Eleventy</a> و<a href="https://www.gatsbyjs.org" rel="external nofollow">Gatsby</a>، فإذا أردت معرفة المزيد حول مولّدات المواقع الساكنة، فراجع دليل تاتيانا ماك <a href="https://www.tatianamac.com/posts/beginner-eleventy-tutorial-parti/" rel="external nofollow">Tatiana Mac</a> للمبتدئين في Eleventy، إذ تشرح في المقال الأول من السلسلة ما هو مولِّد الموقع الساكن، وكيفية ارتباطه بالوسائل الأخرى لنشر محتوى الويب.
</p>

<h2>
	الخلاصة
</h2>

<p>
	لم نعلّمك كتابة أيّ شيفرة برمجية حتى الآن، ولكن نأمل أننا قدّمنا لك خلفيةً مفيدةً حول سبب استخدامك لأطر العمل في المقام الأول وكيفية البدء في الاختيار، كما يبحث مقالنا القادم في أنواع محدَّدة من ميزات أطر العمل، وسبب عملها بطريقة معينة.
</p>

<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Introduction" rel="external nofollow">Introduction to client-side frameworks</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">دليل استخدام سطر الأوامر في عملية تطوير الويب من طرف العميل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%A8%D9%86%D8%A7%D8%A1-%D9%86%D9%85%D9%88%D8%B0%D8%AC-%D9%83%D8%A7%D9%85%D9%84-%D9%84%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1473/" rel="">بناء نموذج كامل لسلسلة أدوات تطوير الويب من طرف العميل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1472/" rel="">أساسيات إدارة الحزم في تطوير الويب من طرف العميل</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1567</guid><pubDate>Sat, 04 Jun 2022 15:04:00 +0000</pubDate></item><item><title>&#x62A;&#x623;&#x62B;&#x64A;&#x631; &#x627;&#x644;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x645;&#x643;&#x62B;&#x641; &#x644;&#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; &#x639;&#x644;&#x649; &#x623;&#x62F;&#x627;&#x621; &#x645;&#x648;&#x627;&#x642;&#x639; &#x627;&#x644;&#x648;&#x64A;&#x628;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D8%A3%D8%AB%D9%8A%D8%B1-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%83%D8%AB%D9%81-%D9%84%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D8%B9%D9%84%D9%89-%D8%A3%D8%AF%D8%A7%D8%A1-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1557/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_05/627f5c186606f_------.png.306723cb070da63541fe4e7309a23a67.png" /></p>

<p>
	يستخدِم المتصفح خيطًا واحدًا افتراضيًا لتنفيذ شيفرة جافاسكربت في صفحتك، إضافةً إلى تخطيط الصفحة وإعادة ضبط العناصر وتجميع الموارد المستهلكة، ويعني هذا أنّ التنفيذ الطويل <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r620/" rel="">لدوال جافاسكربت</a> قد يعيق خيط التنفيذ، والذي يقود بدوره إلى صفحة ضعيفة الاستجابة، وبالتالي تجربة مستخدِم سيئة، كما تستطيع استخدام أداتَي تحليل الأداء Waterfall وFrame rate الموضحتين في مقال <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%A6%D9%8A%D8%B3%D9%8A%D8%A9-%D9%84%D9%84%D8%A3%D8%AF%D8%A7%D8%A9-performance-%D9%84%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A3%D8%AF%D8%A7%D8%A1-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1541/" rel="">المكونات الرئيسية للأداة Performance لتحليل أداء صفحات الويب</a> لمراقبة تنفيذ <a href="https://wiki.hsoub.com/HTML" rel="external">جافاسكربت</a> والمشاكل التي تسببها في الأداء والإشارة إلى دوال محددة تستدعي الانتباه.
</p>

<p>
	سنستخدِم في هذا المقال مثالًا لموقع يسبب فيه التنفيذ الطويل لجافاسكربت عدم الاستجابة، وسنقدِّم بعد ذلك مقاربتين مختلفتين لحل هذه المشكلة، بحيث تقتضي الأولى ضرورة فصل الدوال التي تستغرق وقتًا طويلًا في التنفيذ إلى أجزاء واستخدام الدالة <code>()requestAnimationFrame</code> لجدولة تنفيذ هذه القطع؛ أما الثانية، فهي تنفيذ الدالة كلها في خيط مستقل مستخدِمين عمّال ويب، ولتجريب المثال التطبيقي يمكنك إيجاده في <a href="https://mdn.github.io/performance-scenarios/js-worker/index.html" rel="external nofollow">المستودع الخاص به</a> على جيت هاب، إذ سيبدو هذا الموقع بالصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98661" href="https://academy.hsoub.com/uploads/monthly_2022_05/01_java_script_demo.png.fb3f04400551ec64e187a9c04d255c0b.png" rel=""><img alt="01_java_script_demo.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98661" data-unique="4fq66qat3" src="https://academy.hsoub.com/uploads/monthly_2022_05/01_java_script_demo.png.fb3f04400551ec64e187a9c04d255c0b.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	يتضمن الموقع ثلاث أدوات تحكم:
</p>

<ul>
<li>
		مجموعة أزرار اختيار للتحكم بطريقة تنفيذ <a href="https://academy.hsoub.com/programming/javascript/%D9%86%D9%85%D8%B7-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r785/" rel="">شيفرة جافاسكربت</a>:
	</li>
	<li>
		على أساس كتلة واحدة ضمن الخيط الرئيسي.
	</li>
	<li>
		على أساس سلسلة من عمليات أصغر ضمن الخيط الرئيسي باستخدام <code>()requestAnimationFrame</code>.
	</li>
	<li>
		ضمن خيط مستقل باستخدام عمّال ويب.
	</li>
	<li>
		زر لتنفيذ شيفرة جافاسكربت عنوانه "!Do pointless computations".
	</li>
	<li>
		زر لتشغيل وإيقاف بعض <a href="https://academy.hsoub.com/programming/css/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%B1%D8%B3%D9%88%D9%85-%D9%85%D8%AA%D8%AD%D8%B1%D9%83%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-css-r1340/" rel="">رسوم CSS المتحركة</a> لكي ينفِّذ المتصفح بعض الأعمال خلف الستار.
	</li>
</ul>
<p>
	ابقي الخيار على التنفيذ على أساس كتلة واحدة ضمن الخيط الرئيسي Use blocking call in main thread، واستعد لتسجيل ملف أداء عبر <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%B1%D8%A7%D9%82%D8%A8%D8%A9-%D9%88%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A3%D8%AF%D8%A7%D8%A1-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A3%D8%AF%D8%A7%D8%A9-performance-r1540/" rel="">الأداة Performance</a>.
</p>

<ul>
<li>
		انقر على زر Start animations.
	</li>
	<li>
		ابدأ بتسجيل ملف الأداء.
	</li>
	<li>
		انقر على زر Do pointless computations!‎ مرتين أو ثلاث مرات.
	</li>
	<li>
		أوقف تسجيل الملف.
	</li>
</ul>
<p>
	سيختلف ما تراه ضمن نافذة الأداة Performance تبعًا لحاسوبك، لكنه يبدو مشابهًا للقطة الشاشة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98662" href="https://academy.hsoub.com/uploads/monthly_2022_05/02_perf-js-blocking-overview.png.3c2cdcd06be54f36828495cb8f71e4d0.png" rel=""><img alt="02_perf-js-blocking-overview.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98662" data-unique="6e40ag2rb" src="https://academy.hsoub.com/uploads/monthly_2022_05/02_perf-js-blocking-overview.thumb.png.5383bdb29ef84183c26131f7ebc811de.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	يعرض القسم الأعلى من اللقطة نظرة عامة للأداة Waterfall يخبرنا عن نوع العمليات التي يجريها المتصفح خلال فترة التسجيل، حيث يدل اللون الزهري على أنّ أغلب ما ينفذه المتصفح هي حسابات خاصة <a href="https://academy.hsoub.com/programming/css/%D8%AA%D9%86%D8%B3%D9%8A%D9%82-%D9%86%D8%B5%D9%88%D8%B5-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-css-r251/" rel="">بتنسيق CSS</a> وقد تجد بعض عمليات تخطيط الصفحة وإعادة تدفقها وجمع البيانات المهملة، إذ ينتج ذلك عن رسوميات CSS المتحركة التي تُنفَّذ خلال فترة تسجيل ملف الأداء، وسترى أيضًا كتلًا برتقالية اللون تمثِّل شيفرة جافاسكربت التي تُنفَّذ في كل مرة ننقر فيها الزر، في حين نشاهد في القسم السفلي معدل الإطارات المرتبط بالمسار الزمني للتسجيل، إذ يمكننا رؤية أنّ معدل الإطارات سليم طيلة فترة التسجيل، لكنه ينهار تمامًا عند النقر على الزر، كما سنختار إحدى هذه الفترات ونلقي نظرةً عن قرب على ما يجري من خلال نافذة التفاصيل التي تعرض بيانات الأداة Waterfall:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98663" href="https://academy.hsoub.com/uploads/monthly_2022_05/03_perf_js_blocking_waterfall.png.df8c76e94ff0ea4c2c72d2d5d583a293.png" rel=""><img alt="03_perf_js_blocking_waterfall.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98663" data-unique="zw48onnkg" src="https://academy.hsoub.com/uploads/monthly_2022_05/03_perf_js_blocking_waterfall.thumb.png.f5d68e637d1e9f8fd3df8552c6badd19.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	يُنفِّذ المتصفح دالة جافاسكربت أو سلسلة من الدوال الأصغر عندما نضغط الزر، مما يعيق التنفيذ ضمن الخيط الرئيسي مدة 71.73 ميلي ثانية، إذ يُعَدّ هذا الزمن أعلى بأربع مرات من الزمن المفترض لتنفيذ الإطار أي 16.7 ميلي ثانية، فما الدالة التي سببت المشكلة إذًا؟ لننتقل إلى الأداة Flame Chart لنرى تفاصيلًا أكثر ضمن نافذة التفاصيل:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98664" href="https://academy.hsoub.com/uploads/monthly_2022_05/04_perf_js_blocking_flamechart.png.5d02b853068c93b6b06429a41c12265a.png" rel=""><img alt="04_perf_js_blocking_flamechart.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98664" data-unique="sc1h8rznt" src="https://academy.hsoub.com/uploads/monthly_2022_05/04_perf_js_blocking_flamechart.thumb.png.73bf9a738d23df771a034f2386e0f0ca.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	تعرض لنا الأداة مكدس استدعاء جافاسكربت في لحظة التنفيذ هذه، إذ تتوضع الدالة <code>()calculatePrimes</code> في قمة المكدس، كما يمكننا من خلال التفاصيل المعروضة معرفة اسم الملف الذي يضمها والسطر الذي عُرّفت فيه، وإليك شيفرة الدالة، بالإضافة إلى الدالة التي تستدعيها مباشرةً:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_1627_21" style="">
<span class="kwd">const</span><span class="pln"> iterations </span><span class="pun">=</span><span class="pln"> </span><span class="lit">50</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> multiplier </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1000000000</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> calculatePrimes</span><span class="pun">(</span><span class="pln">iterations</span><span class="pun">,</span><span class="pln"> multiplier</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">var</span><span class="pln"> primes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">var</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> iterations</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">var</span><span class="pln"> candidate </span><span class="pun">=</span><span class="pln"> i </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">multiplier </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">());</span><span class="pln">
    </span><span class="kwd">var</span><span class="pln"> isPrime </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">var</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln"> c </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">(</span><span class="pln">candidate</span><span class="pun">);</span><span class="pln"> </span><span class="pun">++</span><span class="pln">c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">candidate </span><span class="pun">%</span><span class="pln"> c </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="com">// not prime</span><span class="pln">
          isPrime </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
          </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">isPrime</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      primes</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">candidate</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> primes</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> doPointlessComputationsWithBlocking</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">var</span><span class="pln"> primes </span><span class="pun">=</span><span class="pln"> calculatePrimes</span><span class="pun">(</span><span class="pln">iterations</span><span class="pun">,</span><span class="pln"> multiplier</span><span class="pun">);</span><span class="pln">
  pointlessComputationsButton</span><span class="pun">.</span><span class="pln">disabled </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">primes</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ما تنفذه الشيفرة هو اختبار -غير فعال إطلاقًا- لأوّليِّة primality عدد عشوائي كبير ويُكرَّر الاختبار 50 مرة.
</p>

<h2>
	استخدام الدالة requestAnimationFrame
</h2>

<p>
	سنقسم الدالة إلى عدد من الدوال الأصغر والمستقلة بذاتها على أساس محاولة أولى لحل المشكلة، وسنستخدِم بعد ذلك الدالة <code>()requestAnimationFrame</code> لجدولة تنفيذ هذه الأجزاء، إذ تطلب هذه الدالة من المتصفح تنفيذ دالة محددة في كل إطار قبل عملية إعادة رسم الصفحة مباشرةً. وطالما أن الدوال المجتزأة صغيرة بما يكفي، فسيتمكن المتصفح من تنفيذ العمل ضمن الزمن المفترض لكل إطار، كما أنه من السهل تجزئة الدالة <code>()calculatePrimes</code> بحيث تحسب أوّليّة كل عدد في دالة مستقلة:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_1627_23" style="">
<span class="kwd">function</span><span class="pln"> doPointlessComputationsWithRequestAnimationFrame</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="kwd">function</span><span class="pln"> testCandidate</span><span class="pun">(</span><span class="pln">index</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// finishing condition</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">index </span><span class="pun">==</span><span class="pln"> iterations</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">primes</span><span class="pun">);</span><span class="pln">
      pointlessComputationsButton</span><span class="pun">.</span><span class="pln">disabled </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="com">// test this number</span><span class="pln">
    </span><span class="kwd">var</span><span class="pln"> candidate </span><span class="pun">=</span><span class="pln"> index </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">multiplier </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">());</span><span class="pln">
    </span><span class="kwd">var</span><span class="pln"> isPrime </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">var</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln"> c </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">(</span><span class="pln">candidate</span><span class="pun">);</span><span class="pln"> </span><span class="pun">++</span><span class="pln">c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">candidate </span><span class="pun">%</span><span class="pln"> c </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="com">// not prime</span><span class="pln">
          isPrime </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
          </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">isPrime</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      primes</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">candidate</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="com">// schedule the next</span><span class="pln">
    </span><span class="kwd">var</span><span class="pln"> testFunction </span><span class="pun">=</span><span class="pln"> testCandidate</span><span class="pun">.</span><span class="pln">bind</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">,</span><span class="pln"> index </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    window</span><span class="pun">.</span><span class="pln">requestAnimationFrame</span><span class="pun">(</span><span class="pln">testFunction</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">var</span><span class="pln"> primes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
  </span><span class="kwd">var</span><span class="pln"> testFunction </span><span class="pun">=</span><span class="pln"> testCandidate</span><span class="pun">.</span><span class="pln">bind</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">
  window</span><span class="pun">.</span><span class="pln">requestAnimationFrame</span><span class="pun">(</span><span class="pln">testFunction</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	انتقل إلى الخيار Use requestAnimationFrame لاختبار هذا الحل، ثم ابدأ تسجيلًا جديدًا، إذ سيبدو التسجيل هذه المرة مشابهًا للقطة الشاشة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98665" href="https://academy.hsoub.com/uploads/monthly_2022_05/05_perf-js-raf-overview.png.06b21652aac72d8371d1637cbb57c02b.png" rel=""><img alt="05_perf-js-raf-overview.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98665" data-unique="7ay6kcj9j" src="https://academy.hsoub.com/uploads/monthly_2022_05/05_perf-js-raf-overview.thumb.png.ff0c1d304f581eef562d69f5f89c7bb9.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	ما نراه في اللقطة هو ما نتوقعه تمامًا، فبدلًا من ظهور كتلة برتقالية واحدة في كل مرة ننقر فيها على الزر، سنجد سلسلةً طويلةً مكونةً من كتل برتقالية قصيرة جدًا تبدو منفصلة عن بعضها بمقدار إطار -أي زمن تنفيذ إطار- وتمثِّل كل منها تنفيذ دالة واحدة من الدوال التي تستدعيها <code>()requestAnimationFrame</code>، كما يتخلل الكتل البرتقالية كتلًا زهرية اللون تمثِّل عمليات <a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a>، وسيتمكن المتصفح من تنفيذ عمليات الإطار جميعها في الوقت المفترض دون انخفاضات مفاجئة عند نقر الزر -عند تنفيذ جافاسكربت- نظرًا لصغر فترة تنفيذ الدوال المجزَّأة.
</p>

<p>
	حسّن استخدام الدالة <code>()requestAnimationFrame</code> استجابة الموقع، لكن هناك مشكلتَين محتملتَين:
</p>

<ul>
<li>
		من الصعب أحيانًا تقسيم الدالة التي تتطلب وقتًا كبيرًا في التنفيذ إلى دوال مستقلة أصغر، فقد ولَّدت هذه العملية في مثالنا البسيط شيفرةً أعقد.
	</li>
	<li>
		تستغرق الدوال المجزأة وقتًا أطول حتى تُنفَّذ جميعها، إذ يمكن في مثالنا تحديد الوقت الذي استغرقته بدقة: إذ تُكرَّر العملية 50 مرة، ويولّد المتصفح 60 إطار في الثانية، وبالتالي سيستغرق تنفيذ التكرارات الخمسين قرابة الثانية، وهذا الزمن ملحوظ سواء في ملف الأداء أو من قبل المستخدِم.
	</li>
</ul>
<h2>
	استخدام عمال ويب
</h2>

<p>
	سنحاول حل المشكلة الآن باستخدام تقنية عمَّال الويب التي تمكّنك من تنفيذ شيفرة <a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت</a> في خيط مستقل، ولا يمكن بالطبع استدعاء دوال تُنفَّذ ضمن الخيط الرئيسي من خيط العامل أو العكس مباشرةً، وإنما يمكنها التواصل من خلال <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">واجهة برمجية</a> تضمن تراسلًا غير متزامن، وتبدو الشيفرة التي تُنفَّذ ضمن الخيط الرئيسي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1627_33" style="">
<span class="kwd">const</span><span class="pln"> iterations </span><span class="pun">=</span><span class="pln"> </span><span class="lit">50</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> multiplier </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1000000000</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">var</span><span class="pln"> worker </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Worker</span><span class="pun">(</span><span class="str">"js/calculate.js"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> doPointlessComputationsInWorker</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="kwd">function</span><span class="pln"> handleWorkerCompletion</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">message</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">command </span><span class="pun">==</span><span class="pln"> </span><span class="str">"done"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      pointlessComputationsButton</span><span class="pun">.</span><span class="pln">disabled </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
      console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">message</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">primes</span><span class="pun">);</span><span class="pln">
      worker</span><span class="pun">.</span><span class="pln">removeEventListener</span><span class="pun">(</span><span class="str">"message"</span><span class="pun">,</span><span class="pln"> handleWorkerCompletion</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  worker</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"message"</span><span class="pun">,</span><span class="pln"> handleWorkerCompletion</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">);</span><span class="pln">

  worker</span><span class="pun">.</span><span class="pln">postMessage</span><span class="pun">({</span><span class="pln">
    </span><span class="str">"multiplier"</span><span class="pun">:</span><span class="pln"> multiplier</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"iterations"</span><span class="pun">:</span><span class="pln"> iterations
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إنّ الاختلاف الرئيسي في هذه الحالة موازنةً بالسابقة هو الحاجة إلى:
</p>

<ul>
<li>
		إنشاء عامل.
	</li>
	<li>
		إرسال رسالة عندما يحين وقت الحسابات.
	</li>
	<li>
		الإنصات إلى رسالة نصها "done" تشير إلى انتهاء العامل.
	</li>
</ul>
<p>
	نحتاج بعد ذلك إلى ملف يُدعى calculate.js ويضم الشفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1627_31" style="">
<span class="pln">self</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"message"</span><span class="pun">,</span><span class="pln"> go</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> go</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">var</span><span class="pln"> iterations </span><span class="pun">=</span><span class="pln"> message</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">iterations</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">var</span><span class="pln"> multiplier </span><span class="pun">=</span><span class="pln"> message</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">multiplier</span><span class="pun">;</span><span class="pln">
  primes </span><span class="pun">=</span><span class="pln"> calculatePrimes</span><span class="pun">(</span><span class="pln">iterations</span><span class="pun">,</span><span class="pln"> multiplier</span><span class="pun">);</span><span class="pln">

  self</span><span class="pun">.</span><span class="pln">postMessage</span><span class="pun">({</span><span class="pln">
    </span><span class="str">"command"</span><span class="pun">:</span><span class="str">"done"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"primes"</span><span class="pun">:</span><span class="pln"> primes
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> calculatePrimes</span><span class="pun">(</span><span class="pln">iterations</span><span class="pun">,</span><span class="pln"> multiplier</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">var</span><span class="pln"> primes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">var</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> iterations</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">var</span><span class="pln"> candidate </span><span class="pun">=</span><span class="pln"> i </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">multiplier </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">());</span><span class="pln">
    </span><span class="kwd">var</span><span class="pln"> isPrime </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">var</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln"> c </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">(</span><span class="pln">candidate</span><span class="pun">);</span><span class="pln"> </span><span class="pun">++</span><span class="pln">c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">candidate </span><span class="pun">%</span><span class="pln"> c </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="com">// not prime</span><span class="pln">
          isPrime </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
          </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">isPrime</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      primes</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">candidate</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> primes</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لا بد في شيفرة العامل أن ننصت إلى الرسالة التي تخبرنا ببدء التنفيذ، ومن ثم نرسل الرسالة "done" عندما ننهي الحسابات. لاحظ أنّ الشيفرة التي تنفّذ الحسابات هي نفسها تمامًا الشيفرة الأصلية دون تعديل، فكيف سيتغير الأداء الآن؟ انتقل إلى الخيار Use a worker وأنشئ تسجيلًا جديدًا، إذ سيبدو التسجيل مشابهًا للقطة الشاشة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98666" href="https://academy.hsoub.com/uploads/monthly_2022_05/06_perf_js_worker_overview.png.7dc66be94642b3e31336a14d318d3a44.png" rel=""><img alt="06_perf_js_worker_overview.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98666" data-unique="szxdz9jrb" src="https://academy.hsoub.com/uploads/monthly_2022_05/06_perf_js_worker_overview.thumb.png.3c843b5e7a5ed70f214b2424461b99a8.png"></a>
</p>

<p>
	نقرنا الزر في هذا التسجيل ثلاث مرات، حيث تبدو كل نقرة موازنةً بالملف الأول كتلتَين برتقاليتَي اللون وقصيرتين جدًا هما:
</p>

<ul>
<li>
		الدالة <code>()doPointlessComputationsInWorker</code> التي تتعامل مع حدث النقر وتبدأ تنفيذ شيفرة عامل الويب.
	</li>
	<li>
		الدالة <code>()handleWorkerCompletion</code> التي تُنفَّذ عندما ينتهي استدعاء العامل.
	</li>
</ul>
<p>
	سينفِّذ العامل اختبارات الأوّليّة في الفترة الفاصلة بين تنفيذ الدالتين السابقتين ولم يلاحظ أيّ تأثير على استجابة الخيط الرئيسي، وقد يبدو هذا الأمر مستبعدًا بعض الشيء، لكن قد تستغل تقنية عمال الويب إيجابيات المعالجات متعددة النوى كونها تعمل على خيط مختلف، في حين لا يمكن للمواقع التي تعتمد خيطًا واحدًا استغلال هذه الناحية في تحسين الأداء، وتبقى المحدودية الرئيسية لاستخدام عمال ويب هي عدم توفر واجهة برمجية <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">لشجرة DOM</a> لدعم عملياتها.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Tools/Performance/Scenarios/Intensive_JavaScript" rel="external nofollow">Intensive Javascript</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/css/%D8%AA%D8%A3%D8%AB%D9%8A%D8%B1-%D8%B1%D8%B3%D9%88%D9%85-css-%D8%A7%D9%84%D9%85%D8%AA%D8%AD%D8%B1%D9%83%D8%A9-%D8%B9%D9%84%D9%89-%D8%A3%D8%AF%D8%A7%D8%A1-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1555/" rel="">تأثير رسوم CSS المتحركة على أداء مواقع الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AE%D8%B7%D9%88%D8%A7%D8%AA-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D9%91%D8%A9-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A3%D8%AF%D8%A7%D8%A1-%D8%A7%D9%84%D9%85%D9%88%D8%A7%D9%82%D8%B9-r519/" rel="">خطوات أساسيّة لتحسين أداء المواقع</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1557</guid><pubDate>Wed, 18 May 2022 15:07:02 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x639;&#x627;&#x645;&#x644; &#x627;&#x644;&#x62E;&#x62F;&#x645;&#x629; Service Worker &#x644;&#x62A;&#x633;&#x631;&#x64A;&#x639; &#x645;&#x648;&#x642;&#x639;&#x643;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D8%AE%D8%AF%D9%85%D8%A9-service-worker-%D9%84%D8%AA%D8%B3%D8%B1%D9%8A%D8%B9-%D9%85%D9%88%D9%82%D8%B9%D9%83-r1542/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_04/626d1fcde7bb5_----Service-Worker--.png.6b67d5c321c2fb94290518e552ff8045.png" /></p>

<p>
	توفر <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/?msclkid=0ca53fe2c87a11ecbe6e2f9111539a69" rel="">الواجهة البرمجية</a> لعامل الخدمة Service Worker أدوات متعددةً وواسعة الاستخدامات، تتميز بمرونتها وتقديمها لأداء أفضل، إذا لم تستخدم عامل الخدمة سابقًا -ولا يمكن لومك على ذلك لأنه لم يلق تبنيًا واسعًا حتى عام 2020- فإليك طريقة عمله:
</p>

<ol>
<li>
		عند أول زيارة إلى الموقع سيسجل المتصفح وكيلًا من طرف العميل، يعمل على كمية صغيرة من جافاسكربت تعمل في خيط thread خاص بها، مثل عامل الويب.
	</li>
	<li>
		بعد تسجيل عامل الخدمة يمكنك مقاطعة الطلبات الصادرة، وتحديد كيفية الرد عليها في حدث عامل الخدمة <code>()fetch</code>.
	</li>
</ol>
<p>
	ما ستفعله للطلبات التي تُقاطعها يعود لك ويعتمد على موقعك الإلكتروني، يمكنك إعادة كتابة الطلبات، والتخزين المؤقت المسبق للملفات الثابتة أثناء التثبيت، وتقديم ميزة العمل بدون اتصال بالإنترنت، وتوصيل حمولات أصغر من <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> لتقديم أداء أفضل لزوّار الموقع المتكررين، وهو ما سنركز عليه في مقالنا.
</p>

<h2>
	تجاوز الاتصال الضعيف بالشبكة
</h2>

<p>
	سنشرح حالةً عمليةً لتوضيح الفكرة، ففي ولاية ويسكونسون الأميركية قدمت شركة ويكلي تيمبر Weekly Timber خدمات قطع الأشجار ونقلها، وبالطبع ستلعب سرعة أداء الموقع دورًا كبيرًا في عملهم، فمكان عمل الشركة هو مقاطعة واشيرا Waushara في الولاية، وليست جودة الاتصال بالشبكة ووثوقيتها هناك جيدةً، مثل العديد من المناطق الريفية في الولايات المتحدة الأميريكية.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="97349" href="https://academy.hsoub.com/uploads/monthly_2022_04/fig-1.png.98f3808b7665d8acebf79b263ab90037.png" rel=""><img alt="fig-1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="97349" data-unique="rxowmhdic" src="https://academy.hsoub.com/uploads/monthly_2022_04/fig-1.thumb.png.45144a6b4f233374def279581876cb34.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	توضح الخارطة تغطية الشبكة اللاسلكية في مقاطعة واشيرا في ولاية ويسكونسون، حيث تعني المناطق ذات اللون الأسمر سرعات منخفضةً تتراوح بين 3 إلى 9.99 ميجا بت في الثانية، بينما تعني المناطق ذات اللون الأحمر سرعات أبطأ من ذلك، والمناطق ذات اللونين الأزرق الداكن والباهت السرعات الأسرع من ذلك.
</p>

<p>
	تحوي ولاية ويسكونسون أراض زراعيةً شاسعةً، بالإضافة إلى العديد من الغابات، وعندما تحتاج لخدمات شركة لتقطيع الخشب، فستكون أول وجهة للبحث عنها هي جوجل، وسيحدد بطء موقع الشركة ما إذا كنت ستبحث عن شركة أخرى، بسبب جودة الاتصال بالشبكة السيئة هناك.
</p>

<p>
	لم تكن ضرورة إضافة عامل الخدمة لموقع الشركة واضحةً في بادئ الأمر، فما دام موقع الشركة يعمل بسرعة فلا حاجة لتعقيد الأمور، لكن بعد معرفة أن الشركة تخدّم زبائنًا خارج مقاطعة واشيرا، وحتى وسط ويسكونسون، فتضمين عامل خدمة بأبسط المزايا داخل الموقع سيوفر في هذه الحالة سرعة ومرونة استخدام الموقع في المناطق ذات جودة الاتصالات الضعيفة.
</p>

<p>
	اعتمد أول عامل خدمة أضيف إلى الموقع -سنشير إليه لاحقًا بعامل الخدمة "الأساسي standard"- على ثلاث استراتيجيات<a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA-%D9%84%D9%84%D9%88%D9%8A%D8%A8-web-caching-%D8%A7%D9%84%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-r204/?msclkid=e31ef153c87611ec82966db5b5a0452e" rel=""> للتخزين المؤقت</a>:
</p>

<ol>
<li>
		التخزين المؤقت المسبق لملفات جافاسكريبت والتنسيقات الموروثة <a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> لجميع الصفحات عند تثبيت عامل الخدمة بعد إطلاق حدث التحميل load للنافذة.
	</li>
	<li>
		تخديم الملفات الثابتة static من مخزن التخزين المؤقت <code>CacheStorage</code> عند توافرها، فإذا لم تتوافر فستُجلب من الشبكة، ثم تخزَّن تخزينًا مؤقتًا لتُخدم عند الزيارات اللاحقة للموقع.
	</li>
	<li>
		تخديم ملفات HTML من الشبكة أولًا، ثم تخزينها في مخزن التخزين المؤقت CacheStorage، وإذا لم يتوافر الاتصال بالشبكة في الزيارات اللاحقة للموقع فسيُخدم ملف HTML المطلوب من التخزين المؤقت.
	</li>
</ol>
<p>
	الاستراتيجيات السابقة ليست مميزةً أو جديدةً، وهي تقدم الفائدتين التاليتين:
</p>

<ul>
<li>
		إمكانية العمل بدون الاتصال بالشبكة، وهو أمر مفيد في حالات الاتصال الضعيف بالشبكة.
	</li>
	<li>
		رفع أداء تخديم الملفات الثابتة بشكل كبير.
	</li>
</ul>
<p>
	أدى رفع الأداء هذا إلى تحسن بنسبة 42% و 48% لكل من المؤشرين أول طباعة للمحتوى First Contentful Paint، واختصارًا FCP، وأكبر طباعة للمحتوى Largest Contentful Paint، واختصارًا LCP، وهذه الأرقام مبنية على <a href="https://ar.wikipedia.org/wiki/%D9%85%D8%B1%D8%A7%D9%82%D8%A8%D8%A9_%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85_%D8%A7%D9%84%D8%AD%D9%82%D9%8A%D9%82%D9%8A%D8%A9" rel="external nofollow">مراقبة المستخدم الحقيقية RUM</a>. ما يعني أن تلك المكاسب ليست نظريةً فقط، بل هي تحسن حقيقي لأشخاص واقعيين.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="97350" href="https://academy.hsoub.com/uploads/monthly_2022_04/fig-2.png.3478c253f4079765d895382981564ed7.png" rel=""><img alt="fig-2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="97350" data-unique="9ao8wlaqy" src="https://academy.hsoub.com/uploads/monthly_2022_04/fig-2.thumb.png.c62df720a72ea5ffc196a15f0fe8902e.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	يوضح هذا المخطط مدة الطلب/الجواب لأدوات المطور في جوجل كروم، والطلب الموضح هو لملف ثابت من مخزن التخزين المؤقت CacheStorage، حيث استغرق عامل الخدمة 23 ميلي ثانية فقط لتحميل هذا الملف، بسبب عدم الحاجة للاتصال بالشبكة، وإمكانية تحميله من <code>CacheStorage</code> مباشرةً.
</p>

<p>
	تحسن الأداء هو نتيجة تجاوز الاتصال بالشبكة كليًا للملفات الثابتة الموجودة مسبقًا في <code>CacheStorage</code>، خصوصًا ملفات التنسيق المعيقة للتصيير، يمكن تحقيق تحسن شبيه بالأداء السابق بالاعتماد على التخزين المؤقت لطلبات <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/?msclkid=261fad66c87711ecbe9adccd9e2d5670" rel="">HTTP</a>، وسنلاحظ التشابه من حيث الأداء السابق مع FCP و LCP دون الاعتماد على عامل الخدمة نهائيًا.
</p>

<p>
	قد تتساءل عن الفرق إذًا بين <code>CacheStorage</code> والتخزين المؤقت لطلبات HTTP، يكمن الفرق في أن التخزين المؤقت لطلبات HTTP يحتاج -على الأقل في بعض الحالات- لإرسال طلب إلى الخادم للتحقق من حداثة الملف الموجود في التخزين المؤقت، ويمكن حل هذه المشكلة باستخدام القيمة <code>immutable</code> للترويسة Cache-Control، لكن ليس لها دعم واسع حاليًا، ويوجد حل آخر بتعيين قيمة عمرية كبيرة للملفات في <code>max-age</code>، لكن المزيج بين الواجهة البرمجية لعامل الخدمة و<code>CacheStorage</code> يوفر مرونةً أكبر.
</p>

<p>
	نستنتج مما سبق أن أبسط تضمين لعامل الخدمة يمكن أن يقدم تحسينًا في الأداء، وربما أفضل مما توفره ترويسة Cache-Control، ويمكن لعامل الخدمة أيضًا توفير مزايا واحتمالات أكبر، وهذا ما سنشاهده في هذا المقال.
</p>

<h2>
	عامل خدمة أسرع وأفضل
</h2>

<p>
	ليس إنشاء أطر عمل وأنماط جديدة نتبعها نحن المطورون هو الابتكار الحقيقي في عالم الويب، وإنما فائدة هذه الأدوات التي نستعملها بالنسبة للمستخدم الحقيقي لمنتجات تلك الأدوات، إذ يجب أن يكون المستخدم على رأس أولويات المطورين.
</p>

<p>
	توفر الواجهة البرمجية لعامل الخدمة مساحة ابتكار واسعة نسبيًا، تؤدي لأثر كبير على تجربة الويب، ويمكن لبعض الأمور، مثل التحميل المسبق للتنقل ومجرى القراءة <code>ReadableStream</code>، أن تحول عامل الخدمة من أمر جيد إلى سيء، يمكن باستخدام تلك المزايا توفير الإمكانيات التالية على الترتيب:
</p>

<ul>
<li>
		تقليص وقت استجابة عامل الخدمة عبر تمكين العمل على التوازي بين وقت إقلاع عامل الخدمة وإرسال طلبات التنقل.
	</li>
	<li>
		التحكم في تدفق بيانات المحتوى القادم من <code>CacheStorage</code> والشبكة.
	</li>
</ul>
<p>
	وسندمج هذه الإمكانيات للحصول على ميزة جديدة، وهي التخزين المؤقت المسبق لأجزاء الترويسة والتذييل، ثم دمجها مع جزئيات من المحتوى القادم من الشبكة، مما سيقلل من كمية البيانات التي سنحتاج لتحميلها عبر الشبكة، وسيحسن السرعة المدرَكة للموقع في الزيارات المتكررة، ويندرج كل ما سبق تحت تصنيف الابتكار الحقيقي الذي يفيد الجميع.
</p>

<h3>
	تحضير الأساسيات
</h3>

<p>
	تبدو فكرة تجميع أجزاء الترويسة والتذييل في الموقع مع المحتوى القادم من الشبكة شبيهةً بالتطبيقات أحادية الصفحة Single Page Application، واختصارًا SPA، فهي مثلها ستحتاج لتطبيق نموذج "صدفة التطبيق app shell" على موقعك، لكن بدلًا من موجّه من طرف العميل يحاول تجميع المحتوى في قطعة صغيرة واحدة من الترميز، يجب أن تتصور الموقع مثل ثلاث قطع منفصلة:
</p>

<ul>
<li>
		الترويسة
	</li>
	<li>
		المحتوى
	</li>
	<li>
		التذييل
	</li>
</ul>
<p>
	سيبدو ذلك بالشكل التالي في موقع الشركة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="97351" href="https://academy.hsoub.com/uploads/monthly_2022_04/fig-3.png.6bb449e7e86bfbb279ea03f43362a310.png" rel=""><img alt="fig-3.png" class="ipsImage ipsImage_thumbnailed" data-fileid="97351" data-unique="uu3a2zsdh" src="https://academy.hsoub.com/uploads/monthly_2022_04/fig-3.thumb.png.47000cbfcc1002c9ca143b3937ed5e8e.png"></a>
</p>

<p>
	يوضح هذا المخطط ترميزًا لونيًا لأجزاء موقع شركة ويكلي تيمبر، حيث يخزَّن كل من الترويسة والتذييل ضمن <code>CacheStorage</code>، بينما يُجلب المحتوى عبر الشبكة، إلا إذا كان المستخدم غير متصل <a href="https://academy.hsoub.com/devops/networking/%D8%A2%D9%84%D9%8A%D8%A9-%D8%B9%D9%85%D9%84-%D8%B4%D8%A8%D9%83%D8%A9-%D8%A7%D9%84%D8%A5%D9%86%D8%AA%D8%B1%D9%86%D8%AA-r571/?msclkid=72a690bac87711ecab4b83b5c2f97e15" rel="">بالإنترنت</a>.
</p>

<p>
	من الجدير بالذكر هنا أن تلك الأجزاء ليس ترميزها صحيحًا بالضرورة، أي ليس من الضروري أن تكون كل الوسوم مغلقةً ضمن كل جزئية على حدة، المهم فقط أن تنتج تركيبة هذه الجزئيات مع بعضها ترميزًا صحيحًا.
</p>

<p>
	سنبدأ أولًا بالتخزين المؤقت لقسمي الترويسة والتذييل عند تثبيت عامل الخدمة، تخدَّم هذه الأجزاء في موقع الشركة في المسارات <code>partial-header/</code> و<code>partial-footer/</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6266_13" style="">
<span class="pln">self</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"install"</span><span class="pun">,</span><span class="pln"> event </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> cacheName </span><span class="pun">=</span><span class="pln"> </span><span class="str">"اسم للتخزين المؤقت هنا"</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> precachedAssets </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="str">"/partial-header"</span><span class="pun">,</span><span class="pln">  </span><span class="com">// جزئية الترويسة</span><span class="pln">
    </span><span class="str">"/partial-footer"</span><span class="pun">,</span><span class="pln">  </span><span class="com">// جزئية التذييل</span><span class="pln">
    </span><span class="com">// ملفات أخرى نريد تخزينها تخزينًا مؤقتًا</span><span class="pln">
  </span><span class="pun">];</span><span class="pln">

  event</span><span class="pun">.</span><span class="pln">waitUntil</span><span class="pun">(</span><span class="pln">caches</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="pln">cacheName</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">cache </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> cache</span><span class="pun">.</span><span class="pln">addAll</span><span class="pun">(</span><span class="pln">precachedAssets</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}).</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">skipWaiting</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}));</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يجب أن نكون قادرين على جلب محتوى كل صفحة دون الترويسة والتذييل وكذلك معهما، وهذا ضروري لأن عامل الخدمة لن يتحكم بأول زيارة للموقع، لكن عندما يتولى عامل الخدمة يمكننا جلب جزئية المحتوى وتجميعها ضمن استجابة كاملة للصفحة مع الترويسة والتذييل من <code>CacheStorage</code>.
</p>

<p>
	إذا كان موقعك ذا محتوى ثابت، فذلك يعني توليد العديد من جزئيات الترميز التي يمكنك إعادة كتابة طلباتها في حدث عامل الخدمة <code>()fetch</code>، أما إذا احتوى موقعك واجهةً خلفيةً -كما حال موقع الشركة في مثالنا- فيمكنك استخدام ترويسة طلب HTTP لتحدد للخادم ما إذا كنت تريد صفحةً كاملةً أم أجزاء المحتوى فقط.
</p>

<p>
	القسم الأصعب لدينا هو تجميع القطع معًا، وهو ما سنفعله الآن.
</p>

<h3>
	تجميع القطع مع بعضها
</h3>

<p>
	تعَد كتابة عامل خدمة بسيط تحديًا، لكن الأمور ستتعقد بسرعة عند محاولتنا تجميع عدة استجابات معًا، وأحد أسباب ذلك هو أننا سنحتاج لإعداد التحميل المسبق للتنقل لتجاوز الوقت اللازم لإقلاع عامل الخدمة.
</p>

<h4>
	تضمين التحميل المسبق للتنقل
</h4>

<p>
	يعالج التحميل المسبق للتنقل navigation preload مشكلة الوقت اللازم لإقلاع عامل الخدمة، وهو السبب في تأخير طلبات التنقل إلى الشبكة، ولا نريد من عامل الخدمة أن يؤثر على أداء الموقع.
</p>

<p>
	يجب تفعيل التحميل المسبق للتنقل صراحةً، لن يؤخر عامل الخدمة بعد تفعيله طلبات التنقل خلال إقلاعه، ويمكن تفعيل التحميل المسبق للتنقل ضمن حدث عامل الخدمة <code>activate</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6266_15" style="">
<span class="pln">self</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"activate"</span><span class="pun">,</span><span class="pln"> event </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> cacheName </span><span class="pun">=</span><span class="pln"> </span><span class="str">"اسم للتخزين المؤقت هنا"</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> preloadAvailable </span><span class="pun">=</span><span class="pln"> </span><span class="str">"navigationPreload"</span><span class="pln"> in self</span><span class="pun">.</span><span class="pln">registration</span><span class="pun">;</span><span class="pln">

  event</span><span class="pun">.</span><span class="pln">waitUntil</span><span class="pun">(</span><span class="pln">caches</span><span class="pun">.</span><span class="pln">keys</span><span class="pun">().</span><span class="pln">then</span><span class="pun">(</span><span class="pln">keys </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">([</span><span class="pln">
      keys</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">key </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> key </span><span class="pun">!==</span><span class="pln"> cacheName</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}).</span><span class="pln">map</span><span class="pun">(</span><span class="pln">key </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> caches</span><span class="pun">.</span><span class="kwd">delete</span><span class="pun">(</span><span class="pln">key</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}),</span><span class="pln">
      self</span><span class="pun">.</span><span class="pln">clients</span><span class="pun">.</span><span class="pln">claim</span><span class="pun">(),</span><span class="pln">
      preloadAvailable </span><span class="pun">?</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">registration</span><span class="pun">.</span><span class="pln">navigationPreload</span><span class="pun">.</span><span class="pln">enable</span><span class="pun">()</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
    </span><span class="pun">]);</span><span class="pln">
  </span><span class="pun">}));</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يجب أن نتحقق من توفر الميزة بسبب عدم الدعم الواسع للتحميل المسبق للتنقل، وهو ما فعلناه في المثال السابق ،وخزّنا نتيجته في المتغير <code>preloadAvailable</code>.
</p>

<p>
	بالإضافة لاحتياجنا استخدام <code>()Promise.all</code> لجلب عدة عمليات غير متزامنة قبل أن تفعيل عامل الخدمة، من تلك العمليات تنظيف بيانات التخزين المؤقت القديمة، وانتظار كلٍ من<code>()clients.claim</code> -وهي التي تخبر عامل الخدمة بالتحكم حالًا بدلًا من انتظار عملية التنقل القادمة- وعملية تفعيل التحميل المسبق للتنقل.
</p>

<p>
	استخدمنا المعامل الثلاثي عند تفعيل التحميل المسبق للتنقل في المتصفحات التي توفر دعمًا له، وذلك لتجنب رمي الاستثناءات في المتصفحات التي لا تدعم تلك الميزة، ونفعل التحميل المسبق للتنقل إذا كانت قيمة <code>preloadAvailable</code> هي <code>true</code>، أما إن لم تكن كذلك فنمرر قيمةً منطقية بوليانيةً لا تؤثر على قبول التابع <code>()Promise.all</code>.
</p>

<p>
	عند تفعيل التحميل المسبق للتنقل، داخل معالج الحدث <code>()fetch</code> في عامل الخدمة لدينا، يجب أن نكتب شيفرةً تستفيد من جواب الطلب الذي سبق تحميله:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6266_17" style="">
<span class="pln">self</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"fetch"</span><span class="pun">,</span><span class="pln"> event </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> request </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> event</span><span class="pun">;</span><span class="pln">

  </span><span class="com">// تم اختصار شيفرة معالجة الملفات الثابتة للتوضيح</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">

  </span><span class="com">// التحقق فيما إذا كان الطلب لمستند</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">request</span><span class="pun">.</span><span class="pln">mode </span><span class="pun">===</span><span class="pln"> </span><span class="str">"navigate"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> networkContent </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">preloadResponse</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        addResponseToCache</span><span class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">clone</span><span class="pun">());</span><span class="pln">

        </span><span class="kwd">return</span><span class="pln"> response</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">

      </span><span class="kwd">return</span><span class="pln"> fetch</span><span class="pun">(</span><span class="pln">request</span><span class="pun">.</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="str">"X-Content-Mode"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"partial"</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        addResponseToCache</span><span class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">clone</span><span class="pun">());</span><span class="pln">

        </span><span class="kwd">return</span><span class="pln"> response</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">});</span><span class="pln">
    </span><span class="pun">}).</span><span class="kwd">catch</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> caches</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">request</span><span class="pun">.</span><span class="pln">url</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    </span><span class="com">// سنضيف المزيد هنا...</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	مع أن هذه ليست الشيفرة الكاملة للحدث <code>()fetch</code> لعامل الخدمة، إلا أن فيها ما يحتاج الشرح:
</p>

<ol>
<li>
		يُتاح الجواب المحمّل مسبقًا في المتغير <code>event.preloadResponse</code>، وستكون تلك القيمة <code>undefined</code> في المتصفحات التي لا تدعم التحميل المسبق للتنقل، لذا يجب تمرير <code>event.preloadResponse</code> للتابع <a href="https://wiki.hsoub.com/JavaScript/Promise/resolve" rel="external"><code>()Promise.resolve</code></a> لتجنب مشاكل التوافقية تلك.
	</li>
	<li>
		بحسب ناتج الدالة <code>then</code>، إذا كان <code>event.preloadResponse</code> مدعومًا فسنستخدم الجواب المحمّل مسبقًا ونضيفه إلى <code>CacheStorage</code> عبر استدعاء الدالة المساعدة <code>()addResponseToCache</code>، أما إن لم يكن مدعومًا فسنرسل طلبًا عبر الشبكة لجلب جزئية المحتوى عبر طلب <code>()fetch</code>، بتعيين الترويسة المخصصة <code>X-Content-Mode</code> بالقيمة partial.
	</li>
	<li>
		إذا كان الاتصال بالشبكة غير متوفر حاليًا، فنعيد آخر نسخة من جزئية محتوى خُزنت في <code>CacheStorage</code>.
	</li>
	<li>
		نُعيد الجواب -بغض النظر عن مصدره- ونعينه قيمةً للمتغير <code>networkContent</code> الذي سنستخدمه لاحقًا.
	</li>
</ol>
<p>
	عندما يفعَّل التحميل المسبق للتنقل، ستضاف الترويسة <code>Service-Worker-Navigation-Preload</code> بالقيمة <code>true</code> إلى طلبات التنقل، وسنتحقق في النظام الخلفي من هذه الترويسة لإرجاع جزئية المحتوى فقط بدلًا من ترميز الصفحة كاملًة.
</p>

<p>
	لا يتوفر الدعم للتحميل المسبق للتنقل على جميع المتصفحات، لذا سنرسل ترويسةً مختلفةً في تلك الحالات، فسنستخدم في حالة موقع شركة ويكلي تيمبر ترويسةً مخصصةً بالاسم <code>X-Content-Mode</code>، وسنعين الثوابت التالية في الواجهة الخلفية للموقع:
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_6266_19" style="">
<span class="pun">&lt;?</span><span class="pln">php

</span><span class="com">// التحقق فيما إذا كان هذا طلب تنقل مسبق</span><span class="pln">
define</span><span class="pun">(</span><span class="str">"NAVIGATION_PRELOAD"</span><span class="pun">,</span><span class="pln"> isset</span><span class="pun">(</span><span class="pln">$_SERVER</span><span class="pun">[</span><span class="str">"HTTP_SERVICE_WORKER_NAVIGATION_PRELOAD"</span><span class="pun">])</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> stristr</span><span class="pun">(</span><span class="pln">$_SERVER</span><span class="pun">[</span><span class="str">"HTTP_SERVICE_WORKER_NAVIGATION_PRELOAD"</span><span class="pun">],</span><span class="pln"> </span><span class="str">"true"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!==</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">);</span><span class="pln">

</span><span class="com">// التحقق فيما إذا كان هذا طلب صريح لجزئية المحتوى</span><span class="pln">
define</span><span class="pun">(</span><span class="str">"PARTIAL_MODE"</span><span class="pun">,</span><span class="pln"> isset</span><span class="pun">(</span><span class="pln">$_SERVER</span><span class="pun">[</span><span class="str">"HTTP_X_CONTENT_MODE"</span><span class="pun">])</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> stristr</span><span class="pun">(</span><span class="pln">$_SERVER</span><span class="pun">[</span><span class="str">"HTTP_X_CONTENT_MODE"</span><span class="pun">],</span><span class="pln"> </span><span class="str">"partial"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!==</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">);</span><span class="pln">

</span><span class="com">// إذا كان أحد الحالتين صحيحًا فالطلب هو لجزئية المحتوى</span><span class="pln">
define</span><span class="pun">(</span><span class="str">"USE_PARTIAL"</span><span class="pun">,</span><span class="pln"> NAVIGATION_PRELOAD </span><span class="pun">===</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> PARTIAL_MODE </span><span class="pun">===</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln">

</span><span class="pun">?&gt;</span></pre>

<p>
	يمكن بعد ذلك الاستعانة بقيمة الثابت <code>USE_PARTIAL</code> لتحديد نوعية الجواب:
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_6266_21" style="">
<span class="pun">&lt;?</span><span class="pln">php

</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">USE_PARTIAL </span><span class="pun">===</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  require_once</span><span class="pun">(</span><span class="str">"partial-header.php"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

require_once</span><span class="pun">(</span><span class="str">"includes/home.php"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">USE_PARTIAL </span><span class="pun">===</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  require_once</span><span class="pun">(</span><span class="str">"partial-footer.php"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">?&gt;</span></pre>

<p>
	إذا كنت تستخدم التخزين المؤقت لصفحات HTML، فيجب عليك تعيين قيمة للترويسة Vary لأجوبة طلبات HTML لتؤخَذ الترويسة <code>Service-Worker-Navigation-Preload</code>، وفي مثالنا أيضا الترويسة <code>X-Content-Mode</code> للتخزين المؤقت لطلبات HTML بالحسبان، وقد لا تحتاج لذلك إذا لم تستخدم التخزين المؤقت لـ HTML في مشروعك.
</p>

<p>
	بعد أن انتهينا من معالجة التحميل المسبق للتنقل، سننتقل الآن إلى تدفق بيانات أجزاء المحتوى عبر الشبكة وتجميعها مع أجزاء الترويسة والتذييل من <code>CacheStorage</code> داخل استجابة موحّدة يوفرها عامل الخدمة.
</p>

<h4>
	تدفق بيانات أجزاء المحتوى وتجميع ردود الطلبات
</h4>

<p>
	يتوافر كل من الترويسة والتذييل مباشرةً لأنهما موجودان داخل <code>CacheStorage</code> منذ تثبيت عامل الخدمة، لكن ما سيعيقنا هو جزئية المحتوى التي سنحتاج لجلبها عبر الشبكة، لذا من الضروري أن نرسل أو نبث الاستجابة على شكل تدفق لنستطيع إضافة المحتوى تباعًا حال وصوله بأسرع ما يمكن، ويمكننا الاستفادة من <code>ReadableStream</code> لتحقيق ذلك.
</p>

<p>
	يجب الانتباه عند التعامل مع <code>ReadableStream</code>، فقد ينتهي الأمر بالتأثير على الأداء بدل تحسينه إذا أغفلنا بعض الخطوات الهامة، وسيكون التابع الذي يجمع الطلبات معًا كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6266_25" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> mergeResponses </span><span class="pun">(</span><span class="pln">responsePromises</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> readers </span><span class="pun">=</span><span class="pln"> responsePromises</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">responsePromise </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">responsePromise</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">getReader</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  let doneResolve</span><span class="pun">,</span><span class="pln">
      doneReject</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> done </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    doneResolve </span><span class="pun">=</span><span class="pln"> resolve</span><span class="pun">;</span><span class="pln">
    doneReject </span><span class="pun">=</span><span class="pln"> reject</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> readable </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ReadableStream</span><span class="pun">({</span><span class="pln">
    async pull </span><span class="pun">(</span><span class="pln">controller</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> reader </span><span class="pun">=</span><span class="pln"> await readers</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">

      </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> done</span><span class="pun">,</span><span class="pln"> value </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> await reader</span><span class="pun">.</span><span class="pln">read</span><span class="pun">();</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">done</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          readers</span><span class="pun">.</span><span class="pln">shift</span><span class="pun">();</span><span class="pln">

          </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">readers</span><span class="pun">[</span><span class="lit">0</span><span class="pun">])</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            controller</span><span class="pun">.</span><span class="pln">close</span><span class="pun">();</span><span class="pln">
            doneResolve</span><span class="pun">();</span><span class="pln">

            </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
          </span><span class="pun">}</span><span class="pln">

          </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">pull</span><span class="pun">(</span><span class="pln">controller</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        controller</span><span class="pun">.</span><span class="pln">enqueue</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        doneReject</span><span class="pun">(</span><span class="pln">err</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pln"> err</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    cancel </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      doneResolve</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> headers </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Headers</span><span class="pun">();</span><span class="pln">
  headers</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"text/html"</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    done</span><span class="pun">,</span><span class="pln">
    response</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Response</span><span class="pun">(</span><span class="pln">readable</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      headers
    </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أهم ما في التابع السابق:
</p>

<ol>
<li>
		يقبل التابع <code>()mergeResponses</code> الوسيط responsePromises، وهو مصفوفة تحوي كائنات من النوع <code>Response</code>، نحصل عليها إما من التحميل المسبق للتنقل أو <code>()fetch</code> أو <code>()caches.match</code>، وبفرض وجود اتصال بالشبكة ستحوي المصفوفة دومًا على ثلاث أجوبة، اثنان من <code>()caches.match</code> وواحد من الشبكة.
	</li>
	<li>
		قبل أن نرسل الاستجابات داخل المصفوفة <code>responsePromises</code>، يجب أن نربط كل استجابة منها بقارئ واحد، يُستخدم لاحقًا في باني ()ReadableStream ليرسل محتوى كل استجابة منها.
	</li>
	<li>
		نُنشئ وعدًا Promise بالاسم done، نعين داخله تابعي الوعد ()resolve و ()reject للمتغيرات الخارجية doneResolve وdoneReject على التوالي، سيُستخدم المتغيران داخل ()ReadableStream للإشارة إلى نجاح تدفق البيانات من عدمه.
	</li>
	<li>
		تُنشأ النسخة الجديدة من ()ReadableStream بالاسم readable، وعندما تُرسل الاستجابات على شكل تدفق من CacheStorage والشبكة، سيضاف محتوى كل استجابة إلى readable.
	</li>
	<li>
		سيرسل التابع ()pull محتوى أول استجابة في المصفوفة على شكل تدفق، وإذا لم يُلغَ تدفق البيانات لسبب ما، فسيُتجاهل قارئ كل استجابة عبر استدعاء التابع ()shift في مصفوفة الاستجابات حالما ينتهي تدفق بيانات المحتوى كليًا، نكرر هذا الأمر إلى أن لا يبق أي قارئ في المصفوفة.
	</li>
	<li>
		سيُرجع تدفق بيانات الاستجابات المدموجة في استجابة واحدة، وسيُعاد مع الترويسة Content-Type بالقيمة text/html.
	</li>
</ol>
<p>
	توجد طريقة أبسط لذلك وهي استخدام <code>TransformStream</code>، لكنها لا تُدعم في جميع المتصفحات، لذا سنعتمد حاليًا هذه الطريقة.
</p>

<p>
	لنعد الآن إلى الحدث <code>()fetch</code> في عامل الخدمة الذي كتبناه سابقًا، ونطبق داخله التابع <code>()mergeResponses</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6266_27" style="">
<span class="pln">self</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"fetch"</span><span class="pun">,</span><span class="pln"> event </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> request </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> event</span><span class="pun">;</span><span class="pln">

  </span><span class="com">// تم اختصار شيفرة معالجة الملفات الثابتة للتوضيح</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">

  </span><span class="com">// التحقق فيما إذا كان الطلب لمستند</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">request</span><span class="pun">.</span><span class="pln">mode </span><span class="pun">===</span><span class="pln"> </span><span class="str">"navigate"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// تم اختصار شيفرة التحميل المسبق/الجلب من الشبكة.</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">

    </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> done</span><span class="pun">,</span><span class="pln"> response </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> await mergeResponses</span><span class="pun">([</span><span class="pln">
      caches</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">"/partial-header"</span><span class="pun">),</span><span class="pln">
      networkContent</span><span class="pun">,</span><span class="pln">
      caches</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">"/partial-footer"</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">]);</span><span class="pln">

    event</span><span class="pun">.</span><span class="pln">waitUntil</span><span class="pun">(</span><span class="pln">done</span><span class="pun">);</span><span class="pln">
    event</span><span class="pun">.</span><span class="pln">respondWith</span><span class="pun">(</span><span class="pln">response</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	عند نهاية معالج الحدث <code>()fetch</code> نمرر جزأي الترويسة والتذييل من <code>CacheStorage</code> إلى التابع <code>()mergeResponses</code>، ونمرر النتيجة إلى تابع الحدث <code>()fetch</code>، واسمه <code>()respondWith</code>، الذي يخدم الاستجابة المدموجة نيابةً عن عامل الخدمة.
</p>

<h2>
	النتائج النهائية
</h2>

<p>
	نفذنا بالكثير من العمل المعقد والصعب نسبيًا، وقد لا يكون هذا الحل مناسبًا لبنية موقعك، لذا من المهم معرفة هل يعوض تحسن الأداء الناتج ذلك الجهد المبذول، وقد كانت مكاسب الأداء جيدةً في حالة موقع شركة ويكلي تيمبر:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="97353" href="https://academy.hsoub.com/uploads/monthly_2022_04/fig-4.png.9de5ef604ea257eec9a0f328f6f19c3b.png" rel=""><img alt="fig-4.png" class="ipsImage ipsImage_thumbnailed" data-fileid="97353" data-unique="7qla8zu4o" src="https://academy.hsoub.com/uploads/monthly_2022_04/fig-4.thumb.png.a66e251853e39d705c8201c60c732e98.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	يظهر المخطط القيم الوسطية لكل من FPC و LCP لعدة أنواع من عامل الخدمة لموقع ويكلي تيمبر.
</p>

<p>
	يقيس اختبار المحاكاة الأداء ضمن جهاز محدد وجودة اتصال معينة بالشبكة، وقد أجري الاختبار السابق على نسخة تجريبية من الموقع بمحاكاة لهاتف أندرويد نوكيا 2 واتصال "3G سريع" مخنوق داخل أدوات المطور في كروم، واختُبرت كل فئة عشر مرات على الصفحة الرئيسية للموقع، ونستنتج من ذلك ما يلي:
</p>

<ul>
<li>
		لا يوجد عامل خدمة أبدًا أسرع قليلًا من عامل الخدمة الأساسي الذي يستعمل طرائق بسيطة في التخزين المؤقت، وكلاهما أبطأ من عامل الخدمة الذي يبث البيانات، وقد يعود ذلك إلى عملية بدء عامل الخدمة مع ذلك ذلك ستظهر بيانات RUM (مراقبة المستخدم الحقيقية) التي سأعرضها بعد قليل حالة مختلفة.
	</li>
</ul>
<p>
	عبد اللطيف ايمش: لم أفهم شيئًا منها
</p>

<ul>
<li>
		يرتبط كل من LCP و FCP ببعضهما عند عدم استخدام عامل خدمة، أو استخدام عامل خدمة "أساسي"، بسبب كون محتوى الصفحة بسيطًا وتنسيقات CSS صغيرةً للغاية، حيث تكون LCP عادةً الفقرة الافتتاحية داخل الصفحة.
	</li>
	<li>
		تفصل خدمة تدفق البيانات داخل عامل الخدمة FCP عن LCP، لأن جزئية الترويسة تُرسل مباشرةً من CacheStroage.
	</li>
	<li>
		تنقص قيمة كل من FCP وLCP عند تدفق البيانات داخل عامل الخدمة مقارنةً بالحالات الأخرى.
	</li>
</ul>
<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="97355" href="https://academy.hsoub.com/uploads/monthly_2022_04/fig-5.png.79e157d8134139a9111bc20ebdeff9c3.png" rel=""><img alt="fig-5.png" class="ipsImage ipsImage_thumbnailed" data-fileid="97355" data-unique="rwblhqdfx" src="https://academy.hsoub.com/uploads/monthly_2022_04/fig-5.thumb.png.5a8b89089a8c9e07602462c6dcda406b.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	يظهر المخطط القيم الوسطية لكل من FPC و LCP في بيانات الأداء في RUM (مراقبة المستخدم الحقيقي) لعدة أنواع من عامل الخدمة لموقع ويكلي تيمبر.
</p>

<p>
	تظهر فائدة تدفق البيانات في عامل الخدمة لدى المستخدمين الحقيقيين، حيث لوحظ تحسن بقيمة 79% لقيمة FCP مقارنةً بعدم استخدام عامل خدمة أبدًا، وتحسن بقيمة 63% عن استخدام عامل الخدمة "الأساسي"، وقد تحسن الأداء في قيمة LCP أقل من ذلك، حيث لوحظ تحسن كبير بقيمة 41% مقارنةً بعدم استخدام عامل خدمة أبدًا، لكن كانت القيمة أبطأ قليلًا مقارنةً مع استخدام عامل الخدمة "الأساسي".
</p>

<p>
	من المهم النظر إلى النسبة الكبيرة من بيانات الأداء المتوفرة، ولا يكفي النظر إلى المتوسطـ، لننظر إلى نسبة 95% من بيانات أداء FCP وLCP :
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="97357" href="https://academy.hsoub.com/uploads/monthly_2022_04/fig-6.png.5d736a823cbdc1ef46388791885ec15f.png" rel=""><img alt="fig-6.png" class="ipsImage ipsImage_thumbnailed" data-fileid="97357" data-unique="jnxzeir9s" src="https://academy.hsoub.com/uploads/monthly_2022_04/fig-6.thumb.png.228e6176400387d70df71bd7b2096760.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	مخطط لنسبة 95% من بيانات الأداء في RUM لكل من FCP و LCP لعدة أنواع من عامل الخدمة لموقع ويكلي تيمبر.
</p>

<p>
	البيانات السابقة هي أفضل مكان يمكننا من خلاله استنتاج أبطأ أداء، ويمكننا ملاحظة تحسن بنسبة 40% و51% عند استخدام تدفق بيانات المحتوى ضمن عامل الخدمة لكل من FCP وLCP على التوالي مقارنةً بعدم استخدام عامل الخدمة، كما نلاحظ انخفاضَا لهاتين القيمتين بقيمة 19% و 43% على التوالي مقارنةً بعامل الخدمة "الأساسي"، وقد تلاحظ أن هذه البيانات غريبة بعض الشيء عن بيانات المحاكاة السابقة، ويجب أن تتذكر أن بيانات RUM تعتمد على زوّار موقعك، وهم يستخدمون شبكات اتصال متعددةً وأجهزةً مختلفةً.
</p>

<p>
	استفاد كل من مؤشري FCP وLCP من الفوائد التي لا تحصى من فكرة تدفق بيانات المحتوى والتخزين المسبق -في حالة متصفح كروم-، والتخفيف من الترميز المرسل عبر الشبكة من خلال تجميع أجزاء الموقع من <code>CacheStorage</code> والشبكة، وأكبر مؤشر تأثر من ذلك هو FCP، يوضح الفيديو التالي الفروقات بين عدم استخدام عامل خدمة، وبين استخدام عامل الخدمة "الأساسي"، وبين استخدام تدفق البيانات والتخزين المسبق داخل عامل الخدمة:
</p>

<p style="text-align: center;">
	<img alt="fig-7.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="97359" data-unique="8d25j9xld" src="https://academy.hsoub.com/uploads/monthly_2022_04/fig-7.gif.060297917a2bf6fe22298365592c8a34.gif" style="width: 600px; height: auto;"></p>

<p>
	تظهر الفيديوهات الثلاثة اختبارًا للزيارة المتكررة للصفحة الرئيسية في موقع ويكلي تيمبر، على اليسار صفحة لا يتحكم بها عامل الخدمة، فقط التخزين المسبق لـ HTTP، وتظهر على اليمين صفحتان يتحكم بهما عامل الخدمة، مع استخدام CacheStorage.
</p>

<p>
	يمكننا أن نسأل أنفسنا الآن، بما أن هذه الطريقة حققت تحسن الأداء الكبير هذا مع موقع بسيط كهذا، فكيف سيكون التحسن مع المواقع الأكثر تعقيدًا، وماذا سنتوقع من موقع فيه قسم ترويسة وتذييل بحمولة ترميز أكبر من تلك؟
</p>

<h2>
	الخلاصة
</h2>

<p>
	لعملنا السابق مساوئ بلا شك، فمثلًا وضع الترويسة في التخزين المؤقت يعني أنه يجب تحديث عنوان المستند من خلال جافاسكريبت عند كل انتقال عبر تغيير قيمة <code>document.title</code>، كما يجب تعديل حالة التنقل من خلال جافاسكريبت أيضًا لتعكس الصفحة الحالية إذا كنت تنفذ ذلك داخل موقعك، لاحظ أن ذلك لن يؤثر على فهرسة موقعك لأن بوت جوجل Googlebot يزحف إلى الصفحات دون أن يحوي تخزينًا مؤقتًا.
</p>

<p>
	قد تواجه تحديات أيضًا في المواقع التي تحوي على نظام مصادقة، فإذا كانت الترويسة داخل الموقع تظهر المستخدم الذي سجل الدخول حاليًا مثلًا، قد تحتاج لتحديث جزئية ترويسة الموقع الآتية من <code>CacheStorage</code> من خلال جافاسكريبت عند كل تنقّل لتعكس المستخدم الموثّق الحالي، ويمكنك ذلك مثلًا عبر تخزين بيانات أساسية عن المستخدم داخل <code>localStorage</code>، وتحديث الواجهة من تلك البيانات.
</p>

<p>
	ستواجه تحديات أخرى، وسيكون الأمر عائدًا إليك لتحديد المنافع التي سيحصل عليها عميلك مقابل كلفة تطويرها، وتناسب الطريقة التي عرضناها غالبًا مواقع، مثل المدونات، ومواقع التسويق، والمواقع الإخبارية، والمتاجر الالكترونية وغيرها، لكن كل ذلك يعتمد بالنهاية على تحسن الأداء والمردود التي ستحققه من جعل موقعك تطبيقًا بصفحة واحدة SPA، ويكمن الفرق أنك هنا لا تبدل طرق تنقل اختبرت عبر الزمن والمعاناة مع عواقب ذلك، لكنك تحسن تلك الطرق فقط، ويجب أخذ هذه الفكرة بعين الاعتبار في عالمنا الذي أصبح فيه التوجيه بطرف العميل منتشرًا.
</p>

<p>
	قد تتساءل "ماذا عن استخدام Workbox؟" وتساؤلك هذا في مكانه، حيث يبسط Workbox التعامل مع الواجهة البرمجية لعامل الخدمة، ولا خطأ في استخدامه، لكن يفضل دائمًا العمل مع عامل الخدمة مباشرةً، حيث سيكسبك ذلك خبرةً أكبر، وستتعرف على كيفية عمل Workbox أساسًا، لكن استخدام عامل الخدمة صعب بعض الشيء، لذا استعن بـ Workbox إذا كان يناسبك، فهو خيار جيد وأفضل من أطر العمل الأخرى.
</p>

<p>
	الواجهة البرمجية لعامل الخدمة أداة قوية لتقليل كمية الترميز المرسل عبر الشبكة، استفاد منها موقع شركة ويكلي تيمبر ومعظم المواقع التي توجهت لاستخدامه، فأصبح الموقع بسببه أسرع بكثير في أماكن بعيدة داخل ولاية ويسكونسون.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://alistapart.com/article/now-thats-what-i-call-service-worker" rel="external nofollow">Now THAT’S What I Call Service Worker</a> لصاحبه Jeremy Wagner.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D9%81%D9%87%D9%88%D9%85-service-worker-%D9%88%D8%AA%D8%A3%D8%AB%D9%8A%D8%B1%D9%87-%D9%81%D9%8A-%D8%A3%D8%AF%D8%A7%D8%A1-%D9%88%D8%A8%D9%86%D9%8A%D8%A9-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D9%88%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r833/?msclkid=8a2b5334c87911eca0b2d151478661fc" rel="">مفهوم Service Worker وتأثيره في أداء وبنية مواقع وتطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/html/html5/%D8%B2%D9%8A%D8%A7%D8%AF%D8%A9-%D8%B3%D8%B1%D8%B9%D8%A9-%D8%A3%D8%AF%D8%A7%D8%A1-%D8%A7%D9%84%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AA%D9%82%D9%86%D9%8A%D8%A9-pre-fetching-r790/?msclkid=a1cf6374c87911ecad33f737c7900ffa" rel="">زيادة سرعة أداء المواقع باستخدام تقنية pre-fetching</a>
	</li>
</ul>
<p>
	 
</p>
]]></description><guid isPermaLink="false">1542</guid><pubDate>Sat, 30 Apr 2022 11:55:12 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x642;&#x648;&#x627;&#x644;&#x628; &#x627;&#x644;&#x646;&#x635;&#x648;&#x635; Template Literals &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%82%D9%88%D8%A7%D9%84%D8%A8-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-template-literals-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1449/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_01/61eed602b917a_----Template-Literals--(2).png.3f4400357390c28f4dde1f67d1264718.png" /></p>

<p>
	أضاف الإصدار ES6 من معايير ECMAScript الصادر عام 2015 ميزة قالب النص Template Literals إلى لغة <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%A7-%D9%87%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%9F-r524/" rel="">جافاسكربت</a> والتي تعد شكلًا جديدًا لصياغة السلاسل النصية Strings في جافاسكربت مُضيفًا العديد من الإمكانيات الجديدة الفعّالة، كإمكانية إنشاء سلاسل نصية متعدّدة السطور بطريقة سهلة، وإمكانية استخدام المواضع المؤقتة placeholder لتضمين قيم التعابير البرمجية Expressions في السلاسل النصية.
</p>

<p>
	إضافة لوجود ميزة متقدمة تدعى قوالب النصوص الموسومة Tagged Template Literals والتي تسمح لك بإجراء العمليات على التعابير في السلاسل النصية.
</p>

<p>
	تزيد كل هذه الإمكانات من خياراتك كمطور لكتابة السلاسل النصية ببراعة واحترافية، جاعلة إياك قادرًا على إنشاء سلاسل نصية ديناميكية يمكن توظيفها في عناوين الويب URLs أو في الإجراءات التي تعدّل عناصر <a href="https://academy.hsoub.com/programming/html/html-%D9%88-css-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-%D9%83%D9%8A%D9%81-%D8%AA%D9%86%D8%B4%D8%A6-%D9%85%D9%88%D9%82%D8%B9%D8%A7-%D9%85%D9%86-%D8%B9%D8%AF%D8%A9-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r286/" rel="">صفحات HTML</a>.
</p>

<p>
	ستتعرف في هذا المقال على الاختلافات ما بين السلاسل النصية المحصورة بين علامتي اقتباس مفردة أو مزدوجة وقوالب النصوص، كما ستتعرف على الطرق المتعدّدة للتصريح عن سلاسل نصية من أشكال مختلفة بما يتضمن السلاسل الممتدّة على أكثر من سطر والديناميكية منها التي تتغير تبعًا لقيمة متحول أو تعبير ما.
</p>

<p>
	ومن ثمّ ستتعلم عن قوالب النصوص الموسومة وسترى بعض الأمثلة الواقعية لمشاريع باستخدام هذه القوالب.
</p>

<h2>
	التصريح عن السلاسل النصية
</h2>

<p>
	سنستعرض في هذا القسم كيفية التصريح عن <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-strings-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r817/?tab=comments" rel="">السلاسل النصية</a> باستخدام علامات الاقتباس المفردة والمزدوجة، ومن ثم سننتقل لاستعراض كيفية إجراء ذلك في حالة استخدام قوالب النصوص.
</p>

<p>
	يمكنك كتابة سلسلة المحارف في جافاسكربت باستخدام علامات اقتباس مفردة <code>' '</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_8" style="">
<span class="kwd">const</span><span class="pln"> single </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Every day is a good day when you paint.'</span></pre>

<p>
	كما يمكن كتابة سلسلة المحارف باستخدام علامات اقتباس مزدوجة <code>" "</code>:
</p>

<pre class="ipsCode">
const double = "Be so very light. Be a gentle whisper."
</pre>

<p>
	وإجمالًا لا يوجد اختلافات جوهرية ما بين استخدام علامات الاقتباس المفردة أو المزدوجة في جافاسكربت، وذلك على خلاف بعض لغات البرمجة والتي تتيح إمكانية التضمين Interpolation باستخدام إحدى الطريقتين حصرًا، والمقصود بالتضمين التعامل مع المواضع المؤقتة كجزء ديناميكي من سلسلة المحارف.
</p>

<p>
	ويعد استخدام علامات الاقتباس المفردة والمزدوجة خيارًا شخصيًا، كما يمكن استخدامهما معًا بشرط إغلاق أيًا منها بنفس نوع البدء:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_13" style="">
<span class="com">// الإغلاق هنا باستخدام علامة اقتباس مفردة لأننا بدأنا بمثلها</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> single </span><span class="pun">=</span><span class="pln"> </span><span class="str">'"We don\'t make mistakes. We just have happy accidents." - Bob Ross'</span></pre>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_15" style="">
<span class="com">// الإغلاق هنا باستخدام علامة اقتباس مزدوجة لأننا بدأنا بمثلها</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"\"We don't make mistakes. We just have happy accidents.\" - Bob Ross"</span></pre>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_17" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">single</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">double</span><span class="pun">);</span></pre>

<p>
	وسيقوم الإجراء <code>log( )‎</code> بالنتيجة بطباعة نفس سلسلة المحارف على الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_19" style="">
<span class="str">"We don't make mistakes. We just have happy accidents."</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="typ">Bob</span><span class="pln"> </span><span class="typ">Ross</span><span class="pln">
</span><span class="str">"We don't make mistakes. We just have happy accidents."</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="typ">Bob</span><span class="pln"> </span><span class="typ">Ross</span></pre>

<p>
	في حين أنّه تكتب قوالب النصوص عبر حصر السلسة النصية بين علامتي اقتباس مائلتين (`) :
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_23" style="">
<span class="kwd">const</span><span class="pln"> template </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Find</span><span class="pln"> freedom on </span><span class="kwd">this</span><span class="pln"> canvas</span><span class="pun">.`</span></pre>

<p>
	وهي لا تحتاج للإغلاق بعلامة اقتباس مفردة أو مزدوجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_21" style="">
<span class="kwd">const</span><span class="pln"> template </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="str">"We don't make mistakes. We just have happy accidents."</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="typ">Bob</span><span class="pln"> </span><span class="typ">Ross</span><span class="pun">`</span></pre>

<p>
	ولكن لا بدّ من إغلاقها بعلامة اقتباس مائلة كما بدأت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_27" style="">
<span class="kwd">const</span><span class="pln"> template </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Template</span><span class="pln"> literals use the \` character</span><span class="pun">.`</span></pre>

<p>
	وتؤدّي قوالب النصوص كافّة الوظائف التي تؤديها السلاسل النصية العادية، وبالتالي يمكنك استبدال كافّة السلاسل النصية في مشروعك بقوالب نصوص، إلّا أنّ القواعد الأساسية الشائعة في <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D9%84%D9%88%D8%A8-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%88%D8%AA%D8%AD%D9%82%D9%8A%D9%82-%D8%B3%D9%87%D9%88%D9%84%D8%A9-%D9%82%D8%B1%D8%A7%D8%A1%D8%AA%D9%87%D8%A7-r1307/" rel="">كتابة الشيفرات البرمجية</a> تنص على استخدام قوالب النصوص فقط عند الحاجة الفعلية لها لما تقدمه من إمكانات إضافية عن السلاسل النصية العادية، وبالتالي يفضّل استخدام علامات التنصيص المفردة أو المزدوجة فقط لحصر السلاسل النصية البسيطة، وهذا ما سيجعل قراءة واختبار شيفرتك البرمجية أسهل لأي مطوّر آخر.
</p>

<p>
	الآن وبعد معرفتك لكيفية التصريح عن السلاسل النصية باستخدام علامات الاقتباس المفردة والمزدوجة وعلامات الاقتباس المائلة، سننتقل للتعرّف على أول فوائد قوالب النصوص وهي الكتابة على عدّة أسطر.
</p>

<h2>
	كتابة السلاسل النصية على عدّة أسطر
</h2>

<p>
	سنرى بدايةً كيفية التصريح عن السلاسل النصية متعددة الأسطر ما قبل ES6، ثمّ سننتقل لنلاحظ كيف سهّل استخدام قوالب النصوص من هذا الأمر.
</p>

<p>
	فسابقًا، وفي حال رغبت بكتابة سلسلة نصية على عدة أسطر في محرر النصوص، كان لا بدّ من استخدام عملية ربط سلاسل (<code>+</code>) والتي لا تمثّل دائمًا الطريقة الأفضل.
</p>

<p>
	فمثلًا وعلى خلاف الواقع، يبدو أنّ السلسلة النصية التالية ستظهر عند التنفيذ على عدّة أسطر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_30" style="">
<span class="kwd">const</span><span class="pln"> address </span><span class="pun">=</span><span class="pln">
  </span><span class="str">'Homer J. Simpson'</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
  </span><span class="str">'742 Evergreen Terrace'</span><span class="pln"> </span><span class="pun">+</span></pre>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_32" style="">
<span class="pln">  </span><span class="str">'Springfield'</span></pre>

<p>
	ولكن في الواقع الإجراء السابق سيسمح لك بتقسيم السلسلة النصية إلى أجزاء أصغر وكتابتها على عدّة أسطر في محرر النصوص فقط، دون حدوث أي أثر على نتيجة الخرج، بمعنى أن السلسلة ستظهر على الخرج في سطر واحد وبدون مسافة فاصلة (رغم استخدام مسافات بادئة في بداية كل جزء من السلسلة في محرّر النصوص) بين جزء وآخر منها، ويكون ناتج تنفيذ الإجراء الإجراء <code>log( )‎</code> عند تمرير المتغير <code>address</code> بالشّكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_38" style="">
<span class="typ">Homer</span><span class="pln"> J</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Simpson742</span><span class="pln"> </span><span class="typ">Evergreen</span><span class="pln"> </span><span class="typ">TerraceSpringfield</span></pre>

<p>
	كما يمكنك استخدام الخط المائل العكسي <code>\</code> لكتابة السلسلة النصية على عدّة أسطر في محرر النصوص كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_36" style="">
<span class="kwd">const</span><span class="pln"> address </span><span class="pun">=</span><span class="pln">
  </span><span class="str">'</span><span class="typ">Homer</span><span class="pln"> J</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Simpson</span><span class="pln">\
</span><span class="lit">742</span><span class="pln"> </span><span class="typ">Evergreen</span><span class="pln"> </span><span class="typ">Terrace</span><span class="pln">\
  </span><span class="typ">Springfield</span><span class="str">'</span></pre>

<p>
	والميزة هنا أنّ المسافات البادئة ستظهر فعلًا كمسافات فارغة في السلسة النصية الناتجة، إلّا أنّها ستظهر أيضًا على سطر واحد بالشّكل :
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_42" style="">
<span class="typ">Homer</span><span class="pln"> J</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Simpson</span><span class="pln">  </span><span class="lit">742</span><span class="pln"> </span><span class="typ">Evergreen</span><span class="pln"> </span><span class="typ">Terrace</span><span class="pln">  </span><span class="typ">Springfield</span></pre>

<p>
	والحل الوحيد لإنشاء سلاسل نصية على عدّة أسطر عند التنفيذ هو استخدام محرف الانتقال لسطر جديد <code>‎\n</code> أثناء كتابة الرمز، مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_44" style="">
<span class="kwd">const</span><span class="pln"> address </span><span class="pun">=</span><span class="pln">
  </span><span class="str">'Homer J. Simpson\n'</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
  </span><span class="str">'742 Evergreen Terrace\n'</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
  </span><span class="str">'Springfield'</span></pre>

<p>
	وسيظهر الخرج عند التنفيذ بالشّكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_46" style="">
<span class="typ">Homer</span><span class="pln"> J</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Simpson</span><span class="pln">\n
</span><span class="lit">742</span><span class="pln"> </span><span class="typ">Evergreen</span><span class="pln"> </span><span class="typ">Terrace</span><span class="pln">\n
</span><span class="typ">Springfield</span></pre>

<p>
	إلّا أنّ استخدام محرف الانتقال لسطر جديد (<code>‎\n</code>) يمكن ألا يكون أمرًا بديهيًا، فيمكن أن يظن المبرمج أنّ الانتقال لسطر جديد أثناء كتابة الرمز في محرّر النصوص كافيًا لتظهر النتيجة المطلوبة في الخرج، وهذا ما توفره قوالب النصوص؛ فمع استخدامها لن تحتاج لإضافة علامة ربط السلاسل النصية أو محرف انتقال لسطر جديد ولا حتّى خط مائل عكسي، كل ما تحتاجه هو مجرّد الضغط على زر <code>Enter</code> وستظهر السلسلة النصية على عدّة أسطر افتراضيًا، مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_48" style="">
<span class="kwd">const</span><span class="pln"> address </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Homer</span><span class="pln"> J</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Simpson</span><span class="pln">
</span><span class="lit">742</span><span class="pln"> </span><span class="typ">Evergreen</span><span class="pln"> </span><span class="typ">Terrace</span><span class="pln">
</span><span class="typ">Springfield</span><span class="pun">`</span></pre>

<p>
	وبذلك سيكون خرج السلسة النصية تمامًا كما كُتبت، أي في مثالنا بالشّكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_50" style="">
<span class="typ">Homer</span><span class="pln"> J</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Simpson</span><span class="pln">
</span><span class="lit">742</span><span class="pln"> </span><span class="typ">Evergreen</span><span class="pln"> </span><span class="typ">Terrace</span><span class="pln">
</span><span class="typ">Springfield</span></pre>

<p>
	كما أنّ استخدام قوالب النصوص سيحتفظ أثناء التنفيذ بأي مسافات بادئة وضعتها، لذا تجنّب استخدام أي مسافات إلّا في موضعها المطلوب، فمثلًا لدى كتابة الترميز التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_52" style="">
<span class="kwd">const</span><span class="pln"> address </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Homer</span><span class="pln"> J</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Simpson</span><span class="pln">
                 </span><span class="lit">742</span><span class="pln"> </span><span class="typ">Evergreen</span><span class="pln"> </span><span class="typ">Terrace</span><span class="pln">
                 </span><span class="typ">Springfield</span><span class="pun">`</span></pre>

<p>
	فبالرغم من كون طريقة الكتابة السابقة أسهل وأنسب للقراءة، إلّا أنّ الخرج لن يكون كذلك، بل سيكون بالشّكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_59" style="">
<span class="typ">Homer</span><span class="pln"> J</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Simpson</span><span class="pln">
                 </span><span class="lit">742</span><span class="pln"> </span><span class="typ">Evergreen</span><span class="pln"> </span><span class="typ">Terrace</span><span class="pln">
                 </span><span class="typ">Springfield</span></pre>

<p>
	بعد التعرّف على السلاسل متعدّدة الأسطر، فيما يلي سنستعرض كيفية تضمين قيم التعابير البرمجية لحالات التصريح عن السلاسل النصية المختلفة.
</p>

<h2>
	تضمين قيم التعابير البرمجية
</h2>

<p>
	في السابق وقبل إصدار ES6 كان يستخدم علامة ربط السلاسل النصية لإنشاء سلاسل نصية <a href="https://academy.hsoub.com/programming/java/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D8%A7%D9%84%D8%AF%D9%8A%D9%86%D8%A7%D9%85%D9%8A%D9%83%D9%8A%D8%A9-arraylists-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1159/" rel="">ديناميكية</a> باستخدام المتحولات أو التعابير البرمجية (سلاسل نصية ذات قيم متغيرة تبعًا لقيم هذه المتحولات أو التعابير البرمجية)، مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_61" style="">
<span class="kwd">const</span><span class="pln"> method </span><span class="pun">=</span><span class="pln"> </span><span class="str">'concatenation'</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> dynamicString </span><span class="pun">=</span><span class="pln"> </span><span class="str">'This string is using '</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> method </span><span class="pun">+</span><span class="pln"> </span><span class="str">'.'</span></pre>

<p>
	فعند تمرير <code>dynamicString</code> إلى الإجراء <code>log( )‎</code>، نحصل في الخرج عند التنفيذ على النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_66" style="">
<span class="typ">This</span><span class="pln"> string is using concatenation</span><span class="pun">.</span></pre>

<p>
	يمكنك تضمين التعابير البرمجية في المواضع المؤقتة مع استخدام قوالب النصوص، ويعبّر عن المواضع المؤقتة برمجيّا باستخدام الرمز <code>‎${}‎</code>، فيصبح التعامل مع مضمون الأقواس على أنّه جافاسكربت وكل ما هو خارج الأقواس على أنّه سلسلة نصية، كما في المثال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_68" style="">
<span class="kwd">const</span><span class="pln"> method </span><span class="pun">=</span><span class="pln"> </span><span class="str">'interpolation'</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> dynamicString </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">This</span><span class="pln"> string is using $</span><span class="pun">{</span><span class="pln">method</span><span class="pun">}.`</span></pre>

<p>
	وعند تمرير <code>dynamicString</code> إلى التابع <code>log( )‎</code>، نحصل في الخرج عند التنفيذ على النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_70" style="">
<span class="typ">This</span><span class="pln"> string is using interpolation</span><span class="pun">.</span></pre>

<p>
	ويعدّ تضمين القيم في السلاسل النصية لإنشاء عناوين ويب ديناميكية Dynamic URLs من أكثر الأمثلة شيوعًا، لأنّ تنفيذ ذلك باستخدام علامات ربط السلاسل النصية لن يكون بالأمر السهل، فمثلًا الإجراء التالي يعرّف تابعًا لإنشاء السلسلة اللازمة لمنح الوصول لبروتوكول التوثيق OAuth:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_73" style="">
<span class="kwd">function</span><span class="pln"> createOAuthString</span><span class="pun">(</span><span class="pln">host</span><span class="pun">,</span><span class="pln"> clientId</span><span class="pun">,</span><span class="pln"> scope</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> host </span><span class="pun">+</span><span class="pln"> </span><span class="str">'/login/oauth/authorize?client_id='</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> clientId </span><span class="pun">+</span><span class="pln"> </span><span class="str">'&amp;scope='</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> scope
</span><span class="pun">}</span><span class="pln">

createOAuthString</span><span class="pun">(</span><span class="str">'https://github.com'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'abc123'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'repo,user'</span><span class="pun">)</span></pre>

<p>
	وبتمرير هذا التابع إلى تابع <code>log( )‎</code> نحصل في الخرج على عنوان الويب التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_75" style="">
<span class="pln">https</span><span class="pun">:</span><span class="com">//github.com/login/oauth/authorize?client_id=abc123&amp;scope=repo,user</span></pre>

<p>
	ولكن ومع استخدام ميزة التضمين في السلاسل النصية التي تقدمها قوالب النصوص، لن تكون مضطرًا لاستخدام علامات الربط أو تتّبع أماكن بدء وانتهاء السلاسل، وفيما يلي نستعرض نفس المثال السابق ولكن عند بناءه باستخدام قوالب النصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_79" style="">
<span class="kwd">function</span><span class="pln"> createOAuthString</span><span class="pun">(</span><span class="pln">host</span><span class="pun">,</span><span class="pln"> clientId</span><span class="pun">,</span><span class="pln"> scope</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">host</span><span class="pun">}/</span><span class="pln">login</span><span class="pun">/</span><span class="pln">oauth</span><span class="pun">/</span><span class="pln">authorize</span><span class="pun">?</span><span class="pln">client_id</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">clientId</span><span class="pun">}&amp;</span><span class="pln">scope</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">scope</span><span class="pun">}`</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

createOAuthString</span><span class="pun">(</span><span class="str">'https://github.com'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'abc123'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'repo,user'</span><span class="pun">)</span></pre>

<p>
	وهذا ما سيعطي نفس الخرج كما في حالة استخدام علامات ربط السلاسل النصية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_81" style="">
<span class="pln">https</span><span class="pun">:</span><span class="com">//github.com/login/oauth/authorize?client_id=abc123&amp;scope=repo,user</span></pre>

<p>
	كما يمكنك تطبيق الإجراء <code>trim()‎</code> على أي قالب محرفي للتخلص من الفراغات في طرفي السلسلة النصية، في المثال التالي استخدامنا البناء المختصر لتابع يُنشئ عنصر قوائم في HTML برابط مخصّص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_83" style="">
<span class="kwd">const</span><span class="pln"> menuItem </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> link</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln">
  </span><span class="pun">`</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">li</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">a href</span><span class="pun">=</span><span class="str">"${url}"</span><span class="pun">&gt;</span><span class="pln">$</span><span class="pun">{</span><span class="pln">link</span><span class="pun">}&lt;/</span><span class="pln">a</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">li</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">`.</span><span class="pln">trim</span><span class="pun">()</span><span class="pln">

menuItem</span><span class="pun">(</span><span class="str">'https://google.com'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Google'</span><span class="pun">)</span></pre>

<p>
	ومع استخدام الإجراء <code>trim()‎</code> ستُقتص الفراغات من على جانبي القالب المحرفي مما يضمن عرض العنصر بطريقة صحيحة، كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_85" style="">
<span class="pun">&lt;</span><span class="pln">li</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">a href</span><span class="pun">=</span><span class="str">"https://google.com"</span><span class="pun">&gt;</span><span class="typ">Google</span><span class="pun">&lt;/</span><span class="pln">a</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">li</span><span class="pun">&gt;</span></pre>

<p>
	ولا يقتصر التضمين على المتحولات فقط، بل من الممكن تضمين أي تعبير برمجي، كما في مثال إيجاد ناتج جمع عددين التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_87" style="">
<span class="kwd">const</span><span class="pln"> sum </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> y
</span><span class="kwd">const</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> y </span><span class="pun">=</span><span class="pln"> </span><span class="lit">100</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> string </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">The</span><span class="pln"> sum of $</span><span class="pun">{</span><span class="pln">x</span><span class="pun">}</span><span class="pln"> and $</span><span class="pun">{</span><span class="pln">y</span><span class="pun">}</span><span class="pln"> is $</span><span class="pun">{</span><span class="pln">sum</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">)}.`</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">string</span><span class="pun">)</span></pre>

<p>
	يعرّف هذا الترميز البرمجي التابع <code>sum</code>، والمتحولين <code>x</code> و<code>y</code>، ومن ثمّ يستخدم كلًا من التابع والمتحولين في سلسلة نصية، وتكون نتيجة التنفيذ على الخرج بالشّكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_89" style="">
<span class="typ">The</span><span class="pln"> sum of </span><span class="lit">5</span><span class="pln"> and </span><span class="lit">100</span><span class="pln"> is </span><span class="lit">105.</span></pre>

<p>
	ولهذا فائدة كبيرة خاصّة عند استخدام عمليات اتخاذ قرار Ternary Operators، إذ تسمح بتضمين الشروط في السلاسل النصية، مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_91" style="">
<span class="kwd">const</span><span class="pln"> age </span><span class="pun">=</span><span class="pln"> </span><span class="lit">19</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> message </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">You</span><span class="pln"> can $</span><span class="pun">{</span><span class="pln">age </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">21</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> </span><span class="str">'not'</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pun">}</span><span class="pln"> view </span><span class="kwd">this</span><span class="pln"> page</span><span class="pun">`</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span></pre>

<p>
	وفي هذه الحالة ستتغير الرسالة في الخرج تبعًا لقيم المتحول <code>age</code> هل هو أعلى أم أقل من <code>21</code>، وبما أنّ قيمة هذا المتحول في مثالنا هي <code>19</code>، نحصل على الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_93" style="">
<span class="typ">You</span><span class="pln"> can not view </span><span class="kwd">this</span><span class="pln"> page</span></pre>

<p>
	وبذلك تكون كوّنت فكرة حول فوائد قوالب النصوص في تضمين التعابير البرمجية.
</p>

<p>
	وفيما يلي سنتفحّص قدرة قوالب النصوص الموسومة على التعامل مع <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1374/" rel="">التعابير البرمجية</a> الممررّة إلى المواضع المؤقتة.
</p>

<h2>
	قوالب النصوص الموسومة
</h2>

<p>
	يعد استخدام قوالب النصوص الموسومة ميزة جيدة متطورّة لقوالب النصوص، والتي تسمّى أيضًا بالقوالب الموسومة.
</p>

<p>
	ويبدأ القالب الموسوم بتابع وسم يضيف الوسم للقالب المحرفي، مقدمّاً لك المزيد من التحكّم في عمليات التضمين للحصول على سلاسل نصية ديناميكية.
</p>

<p>
	في المثال التالي، ستُنشئ تابع وسم يعمل باستخدام القوالب الموسومة، يتضمّن هذا التابع عاملين، سميّ الأوّل <code>string</code> ويتضمّن السلسلة النصية المجردّة، والثاني هو عامل يستخدم مفهوم Rest Parameters والذي يمكن أن يتضمّن عددًا غير محدّدًا من الوسطاء تمثّل التعابير البرمجية المراد تضمينها في السلسلة النصية. يمكنك التحكّم بهذين العاملين والتنفيذ لرؤية ما سينتج عن تعديل كل منهما، بالشّكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_95" style="">
<span class="kwd">function</span><span class="pln"> tag</span><span class="pun">(</span><span class="pln">strings</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln">expressions</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">strings</span><span class="pun">)</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">expressions</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	استخدم التابع <code>tag</code> كتابع القالب الموسوم ونفذ عملية التحويل على السلسلة النصية كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_99" style="">
<span class="kwd">const</span><span class="pln"> string </span><span class="pun">=</span><span class="pln"> tag</span><span class="pun">`</span><span class="typ">This</span><span class="pln"> is a string </span><span class="kwd">with</span><span class="pln"> $</span><span class="pun">{</span><span class="kwd">true</span><span class="pun">}</span><span class="pln"> and $</span><span class="pun">{</span><span class="kwd">false</span><span class="pun">}</span><span class="pln"> and $</span><span class="pun">{</span><span class="lit">100</span><span class="pun">}</span><span class="pln"> interpolated inside</span><span class="pun">.`</span></pre>

<p>
	وبما أنّ الترميز يتضمن إخراج كل من العاملين <code>strings</code> و<code>expressions</code>، يكون الخرج بالشّكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_101" style="">
<span class="pun">(</span><span class="lit">4</span><span class="pun">)</span><span class="pln"> </span><span class="pun">[</span><span class="str">"This is a string with "</span><span class="pun">,</span><span class="pln"> </span><span class="str">" and "</span><span class="pun">,</span><span class="pln"> </span><span class="str">" and "</span><span class="pun">,</span><span class="pln"> </span><span class="str">" interpolated inside."</span><span class="pln">
</span><span class="pun">(</span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">[</span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">]</span></pre>

<p>
	العامل الأوّل <code>strings</code> عبارة عن شعاع يتضمّن كافّة قوالب النصوص التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_103" style="">
<span class="pun">*</span><span class="pln"> </span><span class="str">"This is a string with "</span><span class="pln">
</span><span class="pun">*</span><span class="pln">  </span><span class="str">" and "</span><span class="pln">
</span><span class="pun">*</span><span class="pln"> </span><span class="str">" and "</span><span class="pln">
</span><span class="pun">*</span><span class="pln"> </span><span class="str">" interpolated inside."</span></pre>

<p>
	كما أنّ الخاصيّة <code>raw</code> متوفرة لهذا الوسيط من خلال استخدام <code>strings.raw</code>، والتي تتعامل مع السلسلة دون أخذ أي سلسلة هروب بالحسبان، فمثلًا يكون التعامل مع <code>‎/n</code> على أنّه محرف عادي ولن يفسر على أنه انتقال لسطر جديد.
</p>

<p>
	العامل الثاني <code>…expressions</code> وهو شعاع rest، ويحوي كافّة التعابير وهي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_109" style="">
<span class="pun">*</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
</span><span class="pun">*</span><span class="pln"> </span><span class="kwd">false</span><span class="pln">
</span><span class="pun">*</span><span class="pln"> </span><span class="lit">100</span></pre>

<p>
	وتمرر السلاسل النصية المجرّدة والتعابير كعوامل لتابع القالب الموسوم <code>tag</code>، ومن الجدير بالملاحظة أنّه ليس من الضروري أن يعيد القالب الموسوم قيم من النوع سلاسل نصية حصرًا، فهو قادر على التعامل معها إلّا أنّه يعيد أي نوع من أنواع المعطيات، فعلى سبيل المثال يمكننا جعل التابع يتجاهل كل شيء ويعيد قيمة خالية <code>null</code> كما في التابع <code>returnsNull</code> التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_107" style="">
<span class="kwd">function</span><span class="pln"> returnsNull</span><span class="pun">(</span><span class="pln">strings</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln">expressions</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">null</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> string </span><span class="pun">=</span><span class="pln"> returnsNull</span><span class="pun">`</span><span class="typ">Does</span><span class="pln"> </span><span class="kwd">this</span><span class="pln"> work</span><span class="pun">?`</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">string</span><span class="pun">)</span></pre>

<p>
	وبتمرير المتحول <code>strings</code> إلى الإجراء <code>log( )‎</code> تكون القيمة المعادة في الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_111" style="">
<span class="kwd">null</span></pre>

<p>
	ويعد تغيير جانبي كل تعبير مضمّن مثالًا على الإجراءات الممكن تنفيذها في القوالب الموسومة، كما في حال رغبتك بإحاطة كل التعابير بأحد وسوم HTML.
</p>

<p>
	مثلًا من الممكن بناء التابع <code>bold</code> الذي يقوم بإضافة <code>&lt;strong&gt;</code> و&lt;‎/strong`&gt; إلى جانبي كل تعبير:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_113" style="">
<span class="kwd">function</span><span class="pln"> bold</span><span class="pun">(</span><span class="pln">strings</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln">expressions</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let finalString </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pln">

  </span><span class="com">//التكرار على كافّة التعابير المضمّنة في السلسة</span><span class="pln">
  expressions</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">value</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    finalString </span><span class="pun">+=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">strings</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]}&lt;</span><span class="pln">strong</span><span class="pun">&gt;</span><span class="pln">$</span><span class="pun">{</span><span class="pln">value</span><span class="pun">}&lt;/</span><span class="pln">strong</span><span class="pun">&gt;`</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">

  </span><span class="com">// إضافة السلسلة المجرّدة النهائية</span><span class="pln">
  finalString </span><span class="pun">+=</span><span class="pln"> strings</span><span class="pun">[</span><span class="pln">strings</span><span class="pun">.</span><span class="pln">length </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">]</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> finalString
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> string </span><span class="pun">=</span><span class="pln"> bold</span><span class="pun">`</span><span class="typ">This</span><span class="pln"> is a string </span><span class="kwd">with</span><span class="pln"> $</span><span class="pun">{</span><span class="kwd">true</span><span class="pun">}</span><span class="pln"> and $</span><span class="pun">{</span><span class="kwd">false</span><span class="pun">}</span><span class="pln"> and $</span><span class="pun">{</span><span class="lit">100</span><span class="pun">}</span><span class="pln"> interpolated inside</span><span class="pun">.`</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">string</span><span class="pun">)</span></pre>

<p>
	يستخدم هذا الترميز الحلقة <code>forEach</code> للمرور على كافّة عناصر الشعاع <code>expressions</code> مضيفًا لكل منها عناصر جعل الخط غامقًا، ويكون الخرج الناتج بالشكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_115" style="">
<span class="typ">This</span><span class="pln"> is a string </span><span class="kwd">with</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">strong</span><span class="pun">&gt;</span><span class="kwd">true</span><span class="pun">&lt;</span><span class="str">/strong&gt; and &lt;strong&gt;false&lt;/</span><span class="pln">strong</span><span class="pun">&gt;</span><span class="pln"> and </span><span class="pun">&lt;</span><span class="pln">strong</span><span class="pun">&gt;</span><span class="lit">100</span><span class="pun">&lt;/</span><span class="pln">strong</span><span class="pun">&gt;</span><span class="pln"> interpolated inside</span><span class="pun">.</span></pre>

<p>
	ولا يوجد سوى عدد قليل من الأمثلة على قوالب النصوص الموسومة في المكتبات الشائعة من جافاسكربت، فمثلَا تستخدم المكتبة <code>graphq1-tag</code> القالب الموسوم <code>gq1</code> لتحويل السلاسل النصية من نتائج الاستعلام <code>GraphQL</code> إلى نمط شجرة البنية المجرّدة the abstract syntax tree (AST)‎ وهو النمط الذي يفهمه <code>GraphQL</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_117" style="">
<span class="kwd">import</span><span class="pln"> gql from </span><span class="str">'graphql-tag'</span><span class="pln">

</span><span class="com">// سجل يقوم بالحصول على اسم وكنية المستخدم ذو الترتيب 5</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> query </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    user</span><span class="pun">(</span><span class="pln">id</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      firstName
      lastName
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span></pre>

<p>
	ومن المكتبات التي تستخدم توابع القوالب الموسومة أيضًا هي <code>styled-components</code>، والتي تمكنّك من إنشاء عناصر تفاعلية جديدة من عناصر DOM العادية عبر تطبيق تنسيقات CSS عليها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_119" style="">
<span class="kwd">import</span><span class="pln"> styled from </span><span class="str">'styled-components'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Button</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> styled</span><span class="pun">.</span><span class="pln">button</span><span class="pun">`</span><span class="pln">
  color</span><span class="pun">:</span><span class="pln"> magenta</span><span class="pun">;</span><span class="pln">
</span><span class="pun">`</span><span class="pln">

      </span><span class="pun">ومن</span><span class="pln"> </span><span class="pun">الآن</span><span class="pln"> </span><span class="pun">وصاعدًا</span><span class="pln"> </span><span class="pun">يمكن</span><span class="pln"> </span><span class="pun">استخدام</span><span class="pln"> </span><span class="pun">الثابت</span><span class="pln"> </span><span class="typ">Button</span><span class="pln"> </span><span class="pun">كمكوّن</span><span class="pln"> </span><span class="pun">مخصّص</span><span class="pln"> custom component</span><span class="com">//</span></pre>

<p>
	ولضمان عدم التعامل مع سلسلة نصية على أنّها سلسلة هروب، يمكنك استخدام الإجراء <code>string.raw</code> في قوالب النصوص الموسومة بالشّكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_121" style="">
<span class="kwd">const</span><span class="pln"> rawString </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">.</span><span class="pln">raw</span><span class="pun">`</span><span class="pln">I want to write </span><span class="pun">/</span><span class="pln">n without it being escaped</span><span class="pun">.`</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">rawString</span><span class="pun">)</span></pre>

<p>
	وهذا ما سيعطي الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5621_126" style="">
<span class="pln">I want to write </span><span class="pun">/</span><span class="pln">n without it being escaped</span><span class="pun">.</span></pre>

<h2>
	الخلاصة
</h2>

<p>
	راجعت في هذا المقال طريقة استخدام كل من علامات الاقتباس المفردة والمزدوجة في السلاسل النصية، كما تعلمّت عن قوالب النصوص العادية منها والموسومة، إذ تؤدي قوالب النصوص الكثير من المهام الشائعة المتعلقة بالسلاسل النصية بسهولة كبيرة عبر إمكانية تضمين التعابير البرمجية أو الكتابة على عدّة أسطر دون استخدام أي علامة ربط للسلاسل النصية أو استخدام سلاسل الهروب.
</p>

<p>
	كما أنّ القوالب المحرفية الموسومة تمثّل ميزة جديدة متطورة والتي قامت العديد من المكتبات الشائعة باستخدامها، مثل GraphQL و styled-components.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/understanding-template-literals-in-javascript" rel="external nofollow">Understanding Template Literals in JavaScript</a> لصاحبه Tania Rascia.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-strings-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r817/" rel="">السلاسل النصية (strings) في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1341/" rel="">كيفية التعامل مع النصوص في البرمجة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1283/" rel="">ترميز النصوص والتعامل مع كائنات الملفات في جافاسكربت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1449</guid><pubDate>Mon, 31 Jan 2022 16:06:00 +0000</pubDate></item><item><title>&#x62A;&#x648;&#x627;&#x628;&#x639; &#x627;&#x644;&#x62A;&#x639;&#x627;&#x628;&#x64A;&#x631; &#x627;&#x644;&#x646;&#x645;&#x637;&#x64A;&#x629; &#x648;&#x627;&#x644;&#x646;&#x635;&#x648;&#x635; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1424/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_01/61d6cc7b4ab14_------.png.5bf9f5a4acad8820ef4df9800fb5429a.png" /></p>

<p>
	سنغطي في هذا الفصل التوابع المتنوعة التي تعمل مع التعابير النمطية بشيء من التفصيل بعد أن غطينا موضوع التعابير النمطية تغطية شاملة بدءًا من مقال <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1399/" rel="">أساسيات التعابير النمطية</a> وحتى مقال <a href="https://academy.hsoub.com/programming/javascript/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regex-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1421/" rel="">كتابة تعابير نمطية متقدمة</a> (إن لم تتطلع عليها، فننصحك بالرجوع إليها أولًا).
</p>

<h2>
	التابع (str.match(regexp
</h2>

<p>
	يبحث هذا التابع عن تطابقات للتعبير <code>regexp</code> في النص <code>str</code>، وله ثلاثة أنماط:
</p>

<p>
	النمط الأول، الراية <code>g</code> غير مفعّلة: يعيد التابع التطابق الأول ضمن مصفوفة، تحوي مجموعات ملتقطةً capturing groups وخصائص، هي موقع التطابق <code>index</code>، والنص الذي نبحث فيه <code>input</code>، وهو النص <code>str</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5839_12" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"I love JavaScript"</span><span class="pun">;</span><span class="pln">

let result </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">/Java(Script)/</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> result</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln">     </span><span class="com">// JavaScript (تطابق كامل)</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> result</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln">     </span><span class="com">// Script (المجموعة الملتقطة الأولى)</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">length </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 2</span><span class="pln">

</span><span class="com">// Additional information:</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">index </span><span class="pun">);</span><span class="pln">  </span><span class="com">// 7 (موقع التطابق)</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">input </span><span class="pun">);</span><span class="pln">  </span><span class="com">// I love JavaScript (النص الأصلي)</span></pre>

<p>
	النمط الثاني، الراية <code>g</code> مفعلة: سيعيد التابع <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-arrays-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r818/" rel="">مصفوفةً</a> تضم كل التطابقات الموجودة في صيغة <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%82%D9%8A%D9%85-%D9%88%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%88%D8%A7%D9%84%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1226/" rel="">قيم نصية</a>، دون مجموعات ملتقطة، أو غيرها من التفاصيل.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5839_10" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"I love JavaScript"</span><span class="pun">;</span><span class="pln">

let result </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">/Java(Script)/</span><span class="pln">g</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> result</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// JavaScript</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">length </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span></pre>

<p>
	النمط الثالث، إذا لم يوجد تطابق فسيعيد التابع القيمة <code>null</code>، سواء استخدمنا الراية <code>g</code> أم لم نستخدمها، انتبه جيدًا إلى أنه لا يعيد مصفوفةً فارغةً عندما لا يجد تطابقات، بل يعيد القيمة <code>null</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5839_14" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"I love JavaScript"</span><span class="pun">;</span><span class="pln">

let result </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">/HTML/</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln"> </span><span class="com">// null</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Error</span></pre>

<p>
	إذا أردنا الحصول على النتيجة في مصفوفة، فيمكننا كتابة الشيفرة على الشكل:
</p>

<pre class="ipsCode">
let result = str.match(regexp) || [];
</pre>

<h2>
	التابع (str.matchAll(regexp
</h2>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		إضافة جديدة إلى اللغة، وقد تحتاج المتصفحات القديمة إلى شيفرات موائمة polyfills لتعويض نقص الدعم فيها.
	</p>
</blockquote>

<p>
	يمثل التابع نسخةً محدثةً ومطورةً عن التابع <code>str.match</code>، ويستخدَم لإيجاد جميع التطابقات وفق المجموعات المحددة، ويختلف عن التابع <code>str.match</code> في ثلاثة أمور، هي:
</p>

<ol>
<li>
		لا يعيد مصفوفةً بل كائنًا قابلًا للتكرار iterable object، ويمكن إنشاء مصفوفة نظامية منه باستخدام <code>Array.from</code>.
	</li>
	<li>
		عند استخدام الراية <code>g</code> يعيد كل تطابق في <a href="https://academy.hsoub.com/programming/javascript/%D9%81%D9%87%D9%85-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r555/" rel="">مصفوفة</a> تحتوي مجموعات.
	</li>
	<li>
		عندما لا يجد تطابقات فلا يعيد <code>null</code>، بل كائنًا فارغًا قابلًا للتكرار.
	</li>
</ol>
<p>
	أمثلة عن استخدامه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5839_16" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">'&lt;h1&gt;Hello, world!&lt;/h1&gt;'</span><span class="pun">;</span><span class="pln">
let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/&lt;(.*?)&gt;/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

let matchAll </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">matchAll</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">matchAll</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ليس مصفوفة بل كائن</span><span class="pln">

matchAll </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Array</span><span class="pun">.</span><span class="pln">from</span><span class="pun">(</span><span class="pln">matchAll</span><span class="pun">);</span><span class="pln"> </span><span class="com">// الآن مصفوفة</span><span class="pln">

let firstMatch </span><span class="pun">=</span><span class="pln"> matchAll</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> firstMatch</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln">  </span><span class="com">// &lt;h1&gt;</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> firstMatch</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln">  </span><span class="com">// h1</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> firstMatch</span><span class="pun">.</span><span class="pln">index </span><span class="pun">);</span><span class="pln">  </span><span class="com">// 0</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> firstMatch</span><span class="pun">.</span><span class="pln">input </span><span class="pun">);</span><span class="pln">  </span><span class="com">// &lt;h1&gt;Hello, world!&lt;/h1&gt;</span></pre>

<p>
	إذا استخدمنا الحلقة <code>for..of</code> للحصول على تطابقات <code>matchAll</code>، فلن نحتاج إلى تحويل الكائن إلى مصفوفة من خلال <code>Array.from</code>.
</p>

<h2>
	التابع (str.split(regexp|substr, limit
</h2>

<p>
	يقسم النص وفقًا لتعبير نمطي (أو نص فرعي)، ويمكن استخدامه دون نص بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5839_18" style="">
<span class="pln">alert</span><span class="pun">(</span><span class="str">'12-34-56'</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">'-'</span><span class="pun">))</span><span class="pln"> </span><span class="com">// array of ['12', '34', '56']</span></pre>

<p>
	كما يمكن التقسيم وفقًا لتعبير نمطي بنفس الأسلوب:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5839_20" style="">
<span class="pln">alert</span><span class="pun">(</span><span class="str">'12, 34, 56'</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">/,\s*/</span><span class="pun">))</span><span class="pln"> </span><span class="com">// array of ['12', '34', '56']</span></pre>

<h2>
	التابع (str.search(regexp
</h2>

<p>
	يعيد التابع موقع التطابق الأول، أو يعيد <code>1-</code> إذا لم يجد تطابقًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5839_22" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"A drop of ink may make a million think"</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">search</span><span class="pun">(</span><span class="pln"> </span><span class="str">/ink/</span><span class="pln">i </span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 10 (first match position)</span></pre>

<p>
	<strong>المحدودية الأكبر للتابع هي إيجاده لأول تطابق فقط.</strong> فإذا أردنا مواقع بقية التطابقات فلا بدّ من استخدام طرق أخرى، مثل البحث عن جميع التطابقات باستخدام <code>matchAll</code>.
</p>

<h2>
	التابع (str.replace(str|regexp, str|func
</h2>

<p>
	وهو التابع الأساسي للبحث والاستبدال، والأكثر فائدةً، ويمكن استخدامه للبحث عن أجزاء من النص دون الحاجة <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1374/" rel="">لتعابير نمطية</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5839_24" style="">
<span class="com">// بدل الشرطة القصيرة بنقطتين متعامدين</span><span class="pln">
alert</span><span class="pun">(</span><span class="str">'12-34-56'</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">"-"</span><span class="pun">,</span><span class="pln"> </span><span class="str">":"</span><span class="pun">))</span><span class="pln"> </span><span class="com">// 12:34-56</span></pre>

<p>
	مع ذلك قد يصعب استخدامه أحيانًا.
</p>

<p>
	<strong>عندما يكون المعامل الأول <code>replace</code> نصًا فسيستبدل التطابق الأول فقط</strong> حيث ستلاحظ في المثال الأول استبدال الشرطة القصيرة الأولى فقط بالنقطتين المتعامدتين، ولإيجاد بقية التطابقات واستبدالها، لا بدّ من استخدام التعبير النمطي <code>g/-/</code> بدلًا من النص <code>"-"</code>، مع التفعيل الإجباري للراية <code>g</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5839_26" style="">
<span class="com">// بدل كل شرطة قصيرة بنقطتين متعامدين</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> </span><span class="str">'12-34-56'</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="pln"> </span><span class="str">/-/</span><span class="pln">g</span><span class="pun">,</span><span class="pln"> </span><span class="str">":"</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">  </span><span class="com">// 12:34:56</span></pre>

<p>
	يمكن استخدام محارف خاصة في الوسيط الثاني كونه النص البديل.
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th style="text-align:right">
				الرمز
			</th>
			<th style="text-align:right">
				ما يفعله ضمن النص replacment
			</th>
		</tr></thead>
<tbody>
<tr>
<td style="text-align:right">
				<code>&amp;$</code>
			</td>
			<td style="text-align:right">
				يمثل نص التطابق
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<code>`$</code>
			</td>
			<td style="text-align:right">
				يمثل النص الواقع قبل نص التطابق.
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<code>'$</code>
			</td>
			<td style="text-align:right">
				يمثل النص الواقع بعد نص التطابق.
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<code>n$</code>
			</td>
			<td style="text-align:right">
				يمثل التطابق ذا الرقم n من مجموعة التطابق (الموجود ضمن قوسي تجميع"()" ) وسنتعرف لاحقًا عليها في فصل: المجموعات الملتقطة.
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<code>‎$&lt;name&gt;‎</code>
			</td>
			<td style="text-align:right">
				يمثل التطابق ذا الاسم name من مجموعة التطابق (الموجود ضمن قوسي تجميع"()" )، وسنتعرف لاحقًا ععليها في فصل: المجموعات الملتقطة.
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<code>$$</code>
			</td>
			<td style="text-align:right">
				يمثل المحرف <code>$</code>
			</td>
		</tr>
</tbody>
</table>
<p>
	إليك مثالًا:
</p>

<pre class="ipsCode">
let str = "John Smith";

// swap first and last name
alert(str.replace(/(john) (smith)/i, '$2, $1')) // Smith, John
</pre>

<p>
	<strong>يمكن أن نمرر دالةً ضمن الوسيط الثاني، إذا كانت الحالة تتطلب ذلك.</strong> وستُستدعى هذه الدالة عند كل تطابق، وستصبح القيمة التي تُعيدها بمثابة النص البديل، أما شكل الدالة فهو:
</p>

<pre class="ipsCode">
func(match, p1, p2, ..., pn, offset, input, groups)
</pre>

<p>
	حيث:
</p>

<ol>
<li>
		<code>match</code>: التطابق.
	</li>
	<li>
		<code>p1, p2, ..., pn</code>: محتويات المجموعات الملتقطة، إن وجدت.
	</li>
	<li>
		<code>offset</code>: موقع التطابق.
	</li>
	<li>
		<code>input</code>: النص الأصلي.
	</li>
	<li>
		<code>groups</code>: كائن يضم المجموعات المُسمّاة.
	</li>
</ol>
<p>
	إذا لم توجد أقواس ضمن <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regular-expressions-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1278/?do=embed" rel="">التعبير النمطي</a> فسيكون لدينا ثلاثة وسطاء فقط <code>(func(str, offset, input</code>.
</p>

<p>
	وسنعرض بعض الأمثلة:
</p>

<ul>
<li>
		تحويل التطابقات إلى أحرف كبيرة:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5839_32" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"html and css"</span><span class="pun">;</span><span class="pln">

let result </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">/html|css/</span><span class="pln">gi</span><span class="pun">,</span><span class="pln"> str </span><span class="pun">=&gt;</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">toUpperCase</span><span class="pun">());</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln"> </span><span class="com">// HTML and CSS</span></pre>

<ul>
<li>
		استبدال كل تطابق بموقعه في النص:
	</li>
</ul>
<pre class="ipsCode">
alert("Ho-Ho-ho".replace(/ho/gi, (match, offset) =&gt; offset)); // 0-3-6
</pre>

<ul>
<li>
		في المثال التالي، ستجد قوسين مفتوحين في التعبير النمطي وبالتالي ستقبل الدالة خمسة وسطاء، الأول للتطابق بأكمله، ثم محتوى القوسين، وبعدهما (لم يستخدما في مثالنا) موقع التطابق والنص الأصلي:
	</li>
</ul>
<pre class="ipsCode">
let str = "John Smith";

let result = str.replace(/(\w+) (\w+)/, (match, name, surname) =&gt; `${surname}, ${name}`);

alert(result); // Smith, John
</pre>

<p>
	يفضل استخدام التفكيك destruction عند وجود مجموعات عدة:
</p>

<pre class="ipsCode">
let str = "John Smith";

let result = str.replace(/(\w+) (\w+)/, (...match) =&gt; `${match[2]}, ${match[1]}`);

alert(result); // Smith, John
</pre>

<p>
	عندما نستخدم المجموعات المسماة، فسيكون الكائن <code>groups</code> مع المجموعات دائمًا في الموقع الأخير، وبالتالي سنحصل عليها بالشكل التالي:
</p>

<pre class="ipsCode">
let str = "John Smith";

let result = str.replace(/(?&lt;name&gt;\w+) (?&lt;surname&gt;\w+)/, (...match) =&gt; {
  let groups = match.pop();

  return `${groups.surname}, ${groups.name}`;
});

alert(result); // Smith, John
</pre>

<p>
	سيتيح لنا استخدام دالة عند استبدال النصوص قدرةً كبيرةً، لأنها ستزودنا بكل المعلومات عن التطابق، ولها القدرة على الوصول إلى المتغيرات الخارجية، وتنفيذ أي شيء نريده.
</p>

<h2>
	التابع (str.replaceAll(str|regexp, str|func
</h2>

<p>
	وله وظيفة التابع str.replace نفسها، مع وجود اختلافين رئيسيين:
</p>

<ol>
<li>
		سيستبدل كل التطابقات الموجودة إذا كان وسيطه الأول نصًا، بينما يستبدل <code>replace</code> التطابق الأول فقط.
	</li>
	<li>
		يعمل تمامًا مثل التابع <code>replace</code> إذا كان وسيطه الأول تعبيرًا نمطيًا والراية <code>g</code> مفعلةً، ويعطي خطأً إذا لم تكن كذلك.
	</li>
</ol>
<p>
	ويستخدم بشكل رئيسي عندما نريد استبدال جميع التطابقات، وإليك مثالًا:
</p>

<pre class="ipsCode">
// استبدل كل الشرطات بنقطتين عموديتين
alert('12-34-56'.replaceAll("-", ":")) // 12:34:56
</pre>

<h2>
	التابع (regexp.exec(str
</h2>

<p>
	يعيد هذا التابع تطابقًا مع نمط إذا وجده ضمن النص، وعلى خلاف التوابع السابقة سيُستدعى من قبل كائن تعبير نمطي <code>regexp</code> وليس من قبل نص <code>str</code>، ويسلك سلوكًا مختلفًا عند تفعيل الراية <code>g</code> أو عدم تفعيلها، فإذا لم تكن هذه الراية مفعلةً فسيعيد التطابق الأول فقط، تمامًا مثل التابع <code>(str.match(regexp</code>، ولن يقدم هذا السلوك أي جديد، ولكن مع وجود الراية <code>g</code>:
</p>

<ul>
<li>
		سيعيد التطابق الأول، ويخزن الموقع الذي يلي التطابق مباشرةً ضمن الخاصية <code>regexp.lastIndex</code>.
	</li>
	<li>
		عندما يُستدعى مجددًا سيبدأ البحث انطلاقًا من الموقع المُخزّّن ضمن الخاصية <code>regexp.lastIndex</code> معيدًا التطابق التالي، وسيخزن الموقع الذي يليه مباشرةً ضمن الخاصية <code>regexp.lastIndex</code> محدّثًا قيمتها.
	</li>
	<li>
		وهكذا يستمر العمل.
	</li>
	<li>
		إذا لم يجد تطابقات فسيعيد القيمة <code>null</code>، ويسند القيمة <code>0</code> إلى الخاصية <code>regexp.lastIndex</code>.
	</li>
</ul>
<p>
	سيعيد الاستدعاء المتكرر لهذا التابع كل التطابقات كما شرحنا في الخطوات السابقة، بينما قبل وجود هذا التابع، كان لا بدّ من استخدام الحلقات للحصول على التطابقات جميعها:
</p>

<pre class="ipsCode">
let str = 'More about JavaScript at https://javascript.info';
let regexp = /javascript/ig;

let result;

while (result = regexp.exec(str)) {
  alert( `Found ${result[0]} at position ${result.index}` );
  // Found JavaScript at position 11, then
  // Found javascript at position 33
}
</pre>

<p>
	سيعمل الأسلوب المتبع في المثال السابق أيضًا، على الرغم من أنّ استخدام التابع <code>str.matchAll</code> سيناسب المتصفحات الحديثة أكثر.
</p>

<p>
	<strong>يمكن استخدام التابع <code>(regexp.exec(str</code> للبحث انطلاقًا من موقع محدد بضبط قيمة الخاصية <code>regexp.lastIndex</code> يدويًا.</strong> وإليك مثالًا:
</p>

<pre class="ipsCode">
let str = 'Hello, world!';

let regexp = /\w+/g; //lastIndex يتجاهل المحرك قيمة الخاصية "g" دون الراية 
regexp.lastIndex = 5; // البحث انطلاقًا من الموقع 5

alert( regexp.exec(str) ); // world
</pre>

<p>
	يفرض وجود الراية <code>y</code> البحث في الموقع المحدد ضمن الخاصية <code>regexp.lastIndex</code> تمامًا، وليس بعده.
</p>

<p>
	لنستبدل الراية <code>y</code> بالراية <code>g</code> في المثال السابق، وسنلاحظ عدم وجود تطابقات:
</p>

<pre class="ipsCode">
let str = 'Hello, world!';

let regexp = /\w+/y;
regexp.lastIndex = 5; // search exactly at position 5

alert( regexp.exec(str) ); // null
</pre>

<p>
	يناسب هذا الأمر الحالات التي نحتاج فيها إلى قراءة شيء ما من نص باستخدام التعابير النمطية انطلاقًا من موقع محدد تمامًا.
</p>

<h2>
	التابع (regexp.test(str
</h2>

<p>
	يتأكد هذا التابع من وجود تطابق، ويعيد إحدى القيمتين <code>true/false</code>، وإليك مثالًا:
</p>

<pre class="ipsCode">
let str = "I love JavaScript";

// ينفذ الاختباران التاليان العمل نفسه
alert( /love/i.test(str) ); // true
alert( str.search(/love/i) != -1 ); // true
</pre>

<p>
	مثال مع جواب سلبي:
</p>

<pre class="ipsCode">
let str = "Bla-bla-bla";

alert( /love/i.test(str) ); // false
alert( str.search(/love/i) != -1 ); // false
</pre>

<p>
	في الحالة التي نفعل فيها الراية <code>g</code>، سيبحث التابع عن الخاصية <code>regexp.lastIndex</code> ويحدّث قيمتها، تمامًا مثل التابع <code>regexp.exec</code>، لذلك يمكن استخدامه للبحث في موقع محدد:
</p>

<pre class="ipsCode">
let regexp = /love/gi;

let str = "I love JavaScript";

// يبدأ البحث من الموقع 10
regexp.lastIndex = 10;
alert( regexp.test(str) ); // false (لا تطابق)
</pre>

<p>
	لاحظ أنه قد يخفق الاختبار المستمر لتعبير نمطي عام على نصوص مختلفة، لأن التابع <code>regexp.exec</code> يستدعي قيمًا متقدمةً للخاصية <code>regexp.lastIndex</code>، وبالتالي قد يبدأ البحث في نص آخر ابتداءً من موقع مختلف عن الصفر.
</p>

<p>
	لاحظ في هذا المثال كيف سنختبر النص ذاته مرتين متتاليتين، وسيخفق الاختبار الثاني:
</p>

<pre class="ipsCode">
let regexp = /javascript/g;  // (regexp just created: regexp.lastIndex=0)

alert( regexp.test("javascript") ); // true (regexp.lastIndex=10 now)
alert( regexp.test("javascript") ); // false
</pre>

<p>
	للالتفاف على هذه المشكلة، يمكننا ضبط قيمة الخاصية <code>regexp.lastIndex</code>على الصفر قبل البدء بكل بحث، أو استخدام توابع النصوص، مثل <code>.../str.match/search</code>، بدلًا من توابع التعابير النمطية، فهي لا تستخدم الخاصية <code>lastIndex</code>.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/regexp-methods" rel="external nofollow">Methods of RegExp and string</a> من سلسلة <a href="https://javascript.info/" rel="external nofollow">The Modern JavaScript Tutorial</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D9%81%D9%87%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D9%82%D8%A8-%D8%A7%D9%84%D8%AA%D8%B1%D8%A7%D8%AC%D8%B9%D9%8A-%D8%A7%D9%84%D9%83%D8%A7%D8%B1%D8%AB%D9%8A-%D9%81%D9%8A-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regex-r1423/" rel="">فهم التعقب التراجعي الكارثي في التعابير النمطية RegEx</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%B7%D8%A7%D8%A8%D9%82%D8%A9-%D8%B9%D8%AF%D8%A9-%D9%85%D8%AC%D9%85%D9%88%D8%B9%D8%A7%D8%AA-%D9%86%D9%85%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regex-r1420/" rel="">مطابقة عدة مجموعات نمطية في التعابير النمطية RegEx</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D8%AD%D8%AF%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D9%83%D9%85%D9%8A%D8%A9-%D9%88%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87%D8%A7-%D9%81%D9%8A-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-r1419/" rel="">المحددات الكمية وأنماط استخدامها في التعابير النمطية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D8%AC%D9%85%D9%88%D8%B9%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%AC%D8%A7%D9%84%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-r1400/" rel="">المجموعات والمجالات في التعابير النمطية</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1424</guid><pubDate>Thu, 27 Jan 2022 07:28:06 +0000</pubDate></item><item><title>&#x641;&#x647;&#x645; &#x627;&#x644;&#x62A;&#x639;&#x642;&#x628; &#x627;&#x644;&#x62A;&#x631;&#x627;&#x62C;&#x639;&#x64A; &#x627;&#x644;&#x643;&#x627;&#x631;&#x62B;&#x64A; &#x641;&#x64A; &#x627;&#x644;&#x62A;&#x639;&#x627;&#x628;&#x64A;&#x631; &#x627;&#x644;&#x646;&#x645;&#x637;&#x64A;&#x629; RegEx</title><link>https://academy.hsoub.com/programming/javascript/%D9%81%D9%87%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D9%82%D8%A8-%D8%A7%D9%84%D8%AA%D8%B1%D8%A7%D8%AC%D8%B9%D9%8A-%D8%A7%D9%84%D9%83%D8%A7%D8%B1%D8%AB%D9%8A-%D9%81%D9%8A-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regex-r1423/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_01/61d6bf3e6b209_------.png.271b6de0150717e9bd26917e9ca50024.png" /></p>

<p>
	قد تبدو بعض <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1374/" rel="">التعابير النمطية</a> regular expressions بسيطةً لكن قد يستغرق تنفيذها وقتًا طويلًا، وقد يسبب توقف محرك JavaScript عن الاستجابة، وسيواجه المطورون عاجلًا أم آجلًا هذا السلوك، ومن أعراضه توقف استجابة محرك تعبير نمطي يعمل جيدًا في بعض الأحيان، عندما يبحث ضمن نص معين مستهلكًا موارد المعالج 100%، حيث سيقترح المتصفح في حالة مثل هذه إيقاف تنفيذ السكربت، وإعادة تحميل الصفحة، وليس جيدًا بالطبع أن يُوقف سكربت JavaScript يعمل في الواجهة الخلفية استجابة عملية من عمليات الخادم، فلا بد إذًا من إلقاء نظرة على ذلك.
</p>

<h2>
	مشكلة انتظار انتهاء التعبير النمطي
</h2>

<p>
	لنفترض وجود نص نريد أن نتحقق من كونه يتألف من كلمات <code>+w\</code> يفصل بينها فراغات اختيارية <code>?s\</code>، حيث ستكون إحدى الطرق الواضحة إنشاء تعبير نمطي يبحث عن كلمة يليها فراغ اختياري <code>?w+\s\</code>، وأخيرًا نضيف المحدد الكمي <code>*</code> لتكرار العملية، ويقود هذا التعبير إلى استخدام التعبير <code>$*(?w+\s\)^</code> الذي يبحث عن كلمة على الأقل بالمواصفات السابقة، بحيث يبدأ البحث من بداية النص <code>^</code> وينتهي بنهايته <code>$</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_6" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/^(\w+\s?)*$/</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"A good string"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// true ناجح</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"Bad characters: $@#"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// false فاشل</span></pre>

<p>
	يبدو أنّ التعبير سيعمل والنتيجة صحيحة، لكنه في نصوص معينة سيستغرق وقتًا طويلًا حتى تتوقف استجابة محرك JavaScript، وتُستهلك موارد المعالج 100%.
</p>

<p>
	قد لا تلاحظ شيئًا إن نفّذت المثال التالي، لأن محرك JavaScript سيتوقف عن الاستجابة، وسيتوقف المتصفح عن التجاوب مع الأحداث، وستتوقف واجهة المستخدم عن العمل (تتيح معظم المتصفحات ميزة التمرير فقط)، وسيقترح المتصفح بعد فترة إعادة تحميل الصفحة، فكن على حذر.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_8" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/^(\w+\s?)*$/</span><span class="pun">;</span><span class="pln">
let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"An input string that takes a long time or even makes this regexp hang!"</span><span class="pun">;</span><span class="pln">

</span><span class="com">// سيأخذ بعض الوقت</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	وعلينا القول -حتى نكون منصفين- بأن بعض محركات <a href="https://academy.hsoub.com/devops/linux/%d9%85%d9%82%d8%af%d9%85%d8%a9-%d9%81%d9%8a-%d8%a7%d9%84%d8%aa%d8%b9%d8%a7%d8%a8%d9%8a%d8%b1-%d8%a7%d9%84%d9%86%d9%85%d8%b7%d9%8a%d8%a9-regular-expressions-r63/" rel="">التعابير النمطية</a> تتعامل مع هذا النوع من البحث بفعالية، فالمحرك "V8" وابتداءً من النسخة 8.8 قادر على ذلك، فلن تتوقف استجابة المتصفح 88 Chrome في حالات مثل هذه، بينما ستتوقف استجابة متصفح Firefox.
</p>

<p>
	السؤال الذي طرح نفسه، ما المشكلة؟ لماذا تتوقف استجابة التعبير النمطي؟
</p>

<p>
	لتوضيح ذلك دعونا نبسّط المثال السابق بإزالة الفراغات <code>?S\</code>، وبالتالي سيصبح التعبير النمطي على الشكل <code>$*(?w+\s\)^</code>، ولتوضيح الأمر أكثر دعونا نستبدل الصنف <code>d\</code> بالصنف <code>w\</code>، وستتوقف مع ذلك استجابة التعبير الجديد أيضًا، فمثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_10" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/^(\d+)*$/</span><span class="pun">;</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"012345678901234567890123456789z"</span><span class="pun">;</span><span class="pln">

</span><span class="com">// انتبه، سيأخذ بعض الوقت</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	ما المشكلة في هذا التعبير النمطي؟
</p>

<p>
	قد يلاحظ القارئ أنّ التعبير <code>*(+d\)</code> غريب بعض الشيء، فوجود المحدد الكمي <code>*</code> يبدو مبالغًا فيه، فإن أردنا عددًا يمكن استخدام <code>d\</code>، ومع ذلك يبدو التعبير الجديد المبسط عمليًا أكثر، لكن سبب بطئه أيضًا لم يتغير، لهذا علينا دراسته بالتفصيل للوقوف على المشكلة، فما الذي يحدث أثناء البحث عن النمط <code>$*(+d\)^</code> ضمن النص <code>123456789z</code>، واختُصر قليلًا للوضوح، ولماذا يستغرق الأمر وقتًا؟
</p>

<p>
	إليك ما يفعله المحرك:
</p>

<p>
	أولًا، يحاول المحرك بدايةً البحث عن محتوى الأقواس، وهي الأعداد <code>+d\</code>، وطالما أنّ <code>+</code> <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D8%AD%D8%AF%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D9%83%D9%85%D9%8A%D8%A9-%D9%88%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87%D8%A7-%D9%81%D9%8A-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-r1419/" rel="">محدد كمي جشع</a> greedy افتراضيًا فسيضم كل الأرقام في النص.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_12" style="">
<span class="pln">\d</span><span class="pun">+.......</span><span class="pln">
</span><span class="pun">(</span><span class="lit">123456789</span><span class="pun">)</span><span class="pln">z</span></pre>

<p>
	عند ضم الأرقام جميعها يعدُّ المحرك أن البحث عن <code>+d\</code> قد أنجز، وأن النتيجة هي <code>123456789</code>، ثم ينتقل بعد ذلك إلى تطبيق المحدد الكمي <code>*</code>، لكن الأرقام في النص قد استهلكت جميعها، فلن يقدم مرتكز البداية <code>^</code> أي شيء، ثم يبحث المحرك عن آخر محارف النمط <code>$</code>، ولن يجده لأنّ المحرف الباقي من النص هو <code>z</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_14" style="">
<span class="pln">           X
\d</span><span class="pun">+........</span><span class="pln">$
</span><span class="pun">(</span><span class="lit">123456789</span><span class="pun">)</span><span class="pln">z</span></pre>

<p>
	ثانيًا، وطالما أنّ التطابق غير موجود فسينقص المُكمِّم <code>+</code> عدد المحارف واحدًا ويعيد البحث، لذلك ستكون نتيجة <code>+d\</code> كل الأرقام عدا الأخير <code>12345678</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_17" style="">
<span class="pln">\d</span><span class="pun">+.......</span><span class="pln">
</span><span class="pun">(</span><span class="lit">12345678</span><span class="pun">)</span><span class="lit">9z</span></pre>

<p>
	ثالثًا، يحاول المحرك الآن البحث في الموقع التالي بعد <code>12345678</code>، وعندها يمكن تطبيق المكمِّم <code>*</code>، وسيعطي النمط <code>*(+d\)</code> تطابقًا جديدًا وهو <code>9</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_19" style="">
<span class="pln">\d</span><span class="pun">+.......</span><span class="pln">\d</span><span class="pun">+</span><span class="pln">
</span><span class="pun">(</span><span class="lit">12345678</span><span class="pun">)(</span><span class="lit">9</span><span class="pun">)</span><span class="pln">z</span></pre>

<p>
	ثم يحاول المحرك من جديد إيجاد آخر محرف من النمط <code>$</code> فلن يجده، بل سيجد المحرف الباقي من النص، وهو <code>z</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_21" style="">
<span class="pln">             X
\d</span><span class="pun">+.......</span><span class="pln">\d</span><span class="pun">+</span><span class="pln">
</span><span class="pun">(</span><span class="lit">12345678</span><span class="pun">)(</span><span class="lit">9</span><span class="pun">)</span><span class="pln">z</span></pre>

<p>
	رابعًا، لن يحصل المحرك على التطابق المطلوب؛ وسيستمر في العودة والتعقب مخفضًا عدد التكرارات وهذا ما يسمى بعملية التعقب التراجعي Backtracking أو التراجع والمطابقة ببساطة، وتجري عملية التعقب التراجعي عادةً بالشكل التالي: يقلل آخر محدد كمي جشع عدد التكرارات حتى يصل إلى الحد الأدنى، ثم يأتي دور المحدد الكمي الجشع الذي يسبقه في إنقاص عدد التكرارات وهكذا، إلى أن يتقصى المحرك كل الحالات الممكنة، وإليك بعض الأمثلة عن هذه الحالات:
</p>

<p>
	العدد الأول مؤلف من 7 أرقام، ثم عدد برقمين:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_23" style="">
<span class="pln">             X
\d</span><span class="pun">+......</span><span class="pln">\d</span><span class="pun">+</span><span class="pln">
</span><span class="pun">(</span><span class="lit">1234567</span><span class="pun">)(</span><span class="lit">89</span><span class="pun">)</span><span class="pln">z</span></pre>

<p>
	العدد الأول من 7 أرقام، ثم عددين كل منهما مكون من رقم واحد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_25" style="">
<span class="pln">               X
\d</span><span class="pun">+......</span><span class="pln">\d</span><span class="pun">+</span><span class="pln">\d</span><span class="pun">+</span><span class="pln">
</span><span class="pun">(</span><span class="lit">1234567</span><span class="pun">)(</span><span class="lit">8</span><span class="pun">)(</span><span class="lit">9</span><span class="pun">)</span><span class="pln">z</span></pre>

<p>
	العدد الأول من 6 أرقام، والثاني من ثلاثة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_29" style="">
<span class="pln">             X
\d</span><span class="pun">+.......</span><span class="pln">\d</span><span class="pun">+</span><span class="pln">
</span><span class="pun">(</span><span class="lit">123456</span><span class="pun">)(</span><span class="lit">789</span><span class="pun">)</span><span class="pln">z</span></pre>

<p>
	العدد الأول من 6 أرقام، يليه عددان آخران:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_32" style="">
<span class="pln">               X
\d</span><span class="pun">+.....</span><span class="pln">\d</span><span class="pun">+</span><span class="pln"> \d</span><span class="pun">+</span><span class="pln">
</span><span class="pun">(</span><span class="lit">123456</span><span class="pun">)(</span><span class="lit">78</span><span class="pun">)(</span><span class="lit">9</span><span class="pun">)</span><span class="pln">z</span></pre>

<p>
	ويوجد عدد كبير من الاحتمالات التي نفصل فيها سلسلةً من الأرقام <code>123456789</code> إلى أعداد، ولنكون أكثر دقة توجد <code>‎2&lt;sup&gt;n&lt;/sup&gt;-1</code> طريقة، حيث <code>n</code> هو طول سلسلة الأرقام، ففي حالة 9 أرقام -كما في حالتنا- لدينا 511 احتمال، أما في حالة 20 رقمًا فلدينا 1048575 احتمال، وبالتالي سيسبب مرور المحرك بهذه الحالات التأخير.
</p>

<h2>
	العودة إلى الكلمات والنصوص
</h2>

<p>
	يحدث الأمر ذاته كما في مثالنا الأول، عندما بحثنا عن كلمات باستخدام النمط <code>$*(?w+\s\)^</code> ضمن النص التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_34" style="">
<span class="typ">An</span><span class="pln"> input that hangs</span><span class="pun">!‎</span></pre>

<p>
	والسبب طبعًا أن الكلمة <code>+w\</code> قد تُمثَّل بعدد كبير من الحالات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_36" style="">
<span class="pun">(</span><span class="pln">input</span><span class="pun">)</span><span class="pln">
</span><span class="pun">(</span><span class="pln">inpu</span><span class="pun">)(</span><span class="pln">t</span><span class="pun">)</span><span class="pln">
</span><span class="pun">(</span><span class="pln">inp</span><span class="pun">)(</span><span class="pln">u</span><span class="pun">)(</span><span class="pln">t</span><span class="pun">)</span><span class="pln">
</span><span class="pun">(</span><span class="pln">in</span><span class="pun">)(</span><span class="pln">p</span><span class="pun">)(</span><span class="pln">ut</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	قد يكون عدم وجود التطابق واضحًا، لأن النص ينتهي بإشارة تعجب، لكن ما يتوقعه التعبير النمطي هو محرف كلمة <code>w\</code> أو فراغ <code>s\</code> في النهاية، وهذا ما لا يعرفه المحرك، إذ سيبحث عن كل الحالات التي يحتمل أن تطابق فيها النمط <code>*(?w+\s\)</code> كل محارف النص، بما في ذلك الحالات التي تضم الفراغ <code>*(w+\s\)</code> أو التي لا تضمها <code>*(+w\)</code>، لأن النمط <code>?s\</code> اختياري، وسيستغرق وقتًا طويلًا نظرًا لوجود عدد كبير من الحالات التي سيستكشفها المحرك، فما العمل؟ هل علينا تفعيل البحث الكسول lazy mode؟
</p>

<p>
	لن يساعدنا ذلك لسوء الحظ، ستتوقف الاستجابة أيضًا إذا استبدلنا النمط <code>?+w\</code> بالنمط <code>+w\</code>، وسيتغير ترتيب الحالات التي سيبحث فيها المحرك فقط، وليس عددها.
</p>

<p>
	تتجنب بعض محركات التعبير النمطي المرور على كل الحالات من خلال بعض الاختبارات، أو استخدام وسائل أتمتة محدودة، أو قد تجعل العملية أكثر سرعةً، ومع ذلك لا تتبع معظم المتصفحات هذه الأساليب، كما أنها لا تساعد دومًا.
</p>

<h2>
	ما هو الحل؟
</h2>

<p>
	توجد مقاربتان لحل المشكلة، الأولى تخفيض عدد الحالات الممكنة، فمثلًا لنجعل المساحة الفارغة إجباريةً، بجعل النمط بالشكل التالي <code>$*w+\s)*\w\)^</code>، أي سنبحث عن أي عدد من الكلمات التي يفصل بينها فراغ، عدا الكلمة الأخيرة فستكون اختيارية <code>w\*</code>، سينتهي البحث سواء وجدت أم لا، انظر إلى التعبير التالي المكافئ للسابق (يحصل على التطابقات نفسها) ويعمل جيدًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_38" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/^(\w+\s)*\w*$/</span><span class="pun">;</span><span class="pln">
let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"An input string that takes a long time or even makes this regex hang!"</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// false</span></pre>

<p>
	لماذا اختفت المشكلة؟ لأن الفراغ بين الكلمات أصبح إجباريًا، فلو حذفنا الفراغ في التعبير السابق فسيقود إلى عدد أكبر من حالات <code>+w\</code> ضمن الكلمة ذاتها، إذ يمكن الحصول على الكلمة <code>input</code> من تكرارين <code>+w\</code> بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_40" style="">
<span class="pln">\w</span><span class="pun">+</span><span class="pln">  \w</span><span class="pun">+</span><span class="pln">
</span><span class="pun">(</span><span class="pln">inp</span><span class="pun">)(</span><span class="pln">ut</span><span class="pun">)</span></pre>

<p>
	لكن النمط الجديد مختلف، فالكلمة متبوعة بفراغ حتمًا <code>*(w+\s\)</code>، وبالتالي لن نحصل على الكلمة من خلال تكرارين للنمط <code>w+\s\</code>، وبهذا لن يهدر المزيد من الوقت في البحث عن كل الحالات الممكنة للحصول على كلمة.
</p>

<h2>
	منع التعقب التراجعي في التعابير النمطية
</h2>

<p>
	لن تساعدنا إعادة كتابة النمط دائمًا، إذ كانت العملية سهلةً وواضحةً في المثال السابق، لكنها عادةً ليست كذلك، كما ستقود إعادة كتابة النمط إلى أنماط أكثر تعقيدًا، وهذا أمر سيء، فالتعابير النمطية معقدة بطبيعتها، لحسن الحظ توجد مقاربة بديلة تقتضي منع التعقب التراجعي backtracking للمحدد الكمي، فأصل المشكلة هو تجربة المحرًك للكثير من الحالات الخاطئة -من وجهة نظرنا طبعًا-، فمن الواضح أنّ تعقب <code>+</code> في النمط <code>$*(+d\)</code> سيسبب مشكلةً، ولن يتغير شيء إن بدّلنا النمط <code>+d+\d\</code> بالنمط <code>+d\</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_42" style="">
<span class="pln">\d</span><span class="pun">+........</span><span class="pln">
</span><span class="pun">(</span><span class="lit">123456789</span><span class="pun">)!</span><span class="pln">

\d</span><span class="pun">+...</span><span class="pln">\d</span><span class="pun">+....</span><span class="pln">
</span><span class="pun">(</span><span class="lit">1234</span><span class="pun">)(</span><span class="lit">56789</span><span class="pun">)!</span></pre>

<p>
	وقد نرغب في مثالنا الأصلي <code>$*(?w+\s\)^</code> بمنع تعقب <code>+w\</code>، لأنها من المفترض أن تبحث عن كلمة كاملة بأكبر طول ممكن، ولا حاجة لتخفيض عدد التكرارات، أو فصلها إلى كلمتين <code>+w+\w\</code> وهكذا.
</p>

<p>
	تدعم محركات التعابير النمطية الحديثة المحددات الكمية الاستحواذية possessive quantifiers عن طريق إضافة الإشارة <code>+</code> بعد المحدد الكمي، أي نضع <code>++d\</code> بدلًا من <code>+d\</code>، وذلك لمنعه من الوقوع في فخ التعقب التراجعي، فالمحددات الكمية الاستحواذية أبسط من النظامية، حيث تطابق ما تستطيع من المحارف دون الوقوع في التعقب التراجعي، وسيكون البحث آنذاك أبسط.
</p>

<p>
	كما يوجد ما يُسمى "المجموعات الذرية الملتقطة" atomic capturing groups، وهو وسيلة لمنع التعقب التراجعي ضمن الأقواس، والخبر السيئ هو أنها غير مدعومة في JavaScript، لكن يمكن تقليدها باستخدام شرط التحقق مما يلي المطابقة lookahead transform.
</p>

<h2>
	البحث عن الخلاص
</h2>

<p>
	لقد وصلنا إلى موضوع متقدم فعلًا، إذ نريد منع المحددات الكمية -مثل <code>+</code>- من التعقب التراجعي، لأن تعقب بعض الأمور غير منطقي على الإطلاق.
</p>

<p>
	إنّ النمط الذي يأخذ أكبر عدد ممكن من تكرارات <code>w\</code> دون تعقب تراجعي هو <code>1\((+w\)=?)</code>، وبالطبع يمكن اختيار أي نمط بدل <code>w\</code>، وقد يبدو النمط غريبًا، لكنه في الواقع تحويل بسيط، لنصفه:
</p>

<ul>
<li>
		سيبحث نمط البحث قُدُمًا <code>=?</code> عن أطول كلمة <code>+w\</code> ابتداءً من الموقع الحالي.
	</li>
	<li>
		لن يتذكر المحرك محتوى ما بين القوسين المسبوق بالمحارف <code>=?</code>، لذلك وضعنا <code>+w\</code> ضمن أقواس، ثم سيتذكر المحرك محتوى القوسين التاليين.
	</li>
	<li>
		ثم نشير إلى الأقواس الخارجية بالرقم <code>1</code>.
	</li>
</ul>
<p>
	سيتقدم البحث إلى الأمام وعند وجود كلمة <code>+w\</code> فسيحددها بالرقم <code>1\</code>، وبكذا سنكون قد صممنا محددًا كميًا استحواذيًا من المحدد الكمي <code>+</code>، حيث يلتقط الكلمة <code>+w\</code> كاملةً فقط، وليس جزءًا منها، فيمكن مثلًا الحصول على الكلمة <code>Java</code> من الكلمة <code>JavaScript</code>، وترك الكلمة <code>Script</code> لتتطابق مع بقية النمط، وإليك موازنةً بين نمطين:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_44" style="">
<span class="pln">alert</span><span class="pun">(</span><span class="pln"> </span><span class="str">"JavaScript"</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">/\w+Script/</span><span class="pun">));</span><span class="pln"> </span><span class="com">// JavaScript</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> </span><span class="str">"JavaScript"</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">/(?=(\w+))\1Script/</span><span class="pun">));</span><span class="pln"> </span><span class="com">// null</span></pre>

<ol>
<li>
		في الحالة الأولى: سنحصل على الكلمة كاملةً، لكن المحدد الكمي سيتعقب بقية النمط متراجعًا محرفًا محرفًا، محاولًا إيجاد بقية النمط، ثم سينجح أخيرًا، عندما يتطابق النمط <code>+w\</code> الكلمة <code>Java</code>.
	</li>
	<li>
		في الحالة الثانية: سيجري البحث قُدمًا وسيجد الكلمة <code>JavaScript</code> كاملةً، وسيحددها بالرقم <code>1</code>، وبالتالي لا طريقة بعد ذلك لإيجاد الكلمة <code>Script</code>.
	</li>
</ol>
<p>
	يمكن استخدام تعابير نمطية أكثر تعقيدًا من <code>w\</code> ضمن <code>1\((+w\)=?)</code> عندما نريد منع التعقب التراجعي للمحدد الكمي <code>+</code>.
</p>

<p>
	لنكتب مثالنا الأول باستخدام التحقق مما يلي التطابق لمنع التعقب التراجعي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_46" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/^((?=(\w+))\2\s?)*$/</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"A good string"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"An input string that takes a long time or even makes this regex hang!"</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// false, يعمل وبسرعة</span></pre>

<p>
	وضعنا <code>2\</code> بدلًا من الرقم <code>1\</code> لوجود أقواس خارجية إضافية، كما يمكننا تسمية الأقواس أيضًا <code>(+&lt;word&gt;\w&gt;?)</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5448_48" style="">
<span class="com">//  ?&lt;word&gt;وتُسمى الأقواس كالتالي, \k&lt;word&gt;يشار إلى الأقواس كالتالي </span><span class="pln">
let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/^((?=(?&lt;word&gt;\w+))\k&lt;word&gt;\s?)*$/</span><span class="pun">;</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"An input string that takes a long time or even makes this regex hang!"</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// false</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"A correct string"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span></pre>

<h2>
	خلاصة
</h2>

<p>
	تُسمى المشكلة التي وصفناها في هذا المقال بالتعقب التراجعي الكارثي، وغطينا طريقتين لحلها:
</p>

<ul>
<li>
		تخفيض عدد الحالات الممكنة التي تتطابق مع نمط إلى الحد الأدنى.
	</li>
	<li>
		منع التعقب التراجعي.
	</li>
</ul>
<p>
	ترجمة -وبتصرف- للفصل <a href="https://javascript.info/regexp-catastrophic-backtracking" rel="external nofollow">Catastrophic backtracking</a> من سلسلة <a href="https://javascript.info/" rel="external nofollow">The Modern JavaScript Tutorial</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1399/" rel="">أساسيات البحث باستخدام التعابير النمطية في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D8%AC%D9%85%D9%88%D8%B9%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%AC%D8%A7%D9%84%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-r1400/" rel="">المجموعات والمجالات في التعابير النمطية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regexppcre-%D9%81%D9%8A-php-r1085/" rel="">التعابير النمطية (regexp/PCRE) في PHP</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1423</guid><pubDate>Thu, 06 Jan 2022 10:46:31 +0000</pubDate></item><item><title>&#x62A;&#x648;&#x641;&#x64A;&#x631; &#x62A;&#x62C;&#x631;&#x628;&#x629; &#x62A;&#x62B;&#x628;&#x64A;&#x62A; &#x645;&#x62E;&#x635;&#x635;&#x629; &#x62F;&#x627;&#x62E;&#x644; &#x62A;&#x637;&#x628;&#x64A;&#x642; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x627;&#x644;&#x62A;&#x642;&#x62F;&#x645;&#x64A; PWA</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D9%88%D9%81%D9%8A%D8%B1-%D8%AA%D8%AC%D8%B1%D8%A8%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D9%85%D8%AE%D8%B5%D8%B5%D8%A9-%D8%AF%D8%A7%D8%AE%D9%84-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%85%D9%8A-pwa-r1445/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_01/61f23fca876cc_--------PWA_(1).png.fcebb20e0479a3257c6dc37f13796b61.png" /></p>

<p>
	تتيح معظم <a href="https://academy.hsoub.com/programming/javascript/%D8%B9%D9%84%D8%A7%D9%82%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%A8%D8%AA%D8%B7%D9%88%D8%B1-%D8%A7%D9%84%D8%A5%D9%86%D8%AA%D8%B1%D9%86%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1310/" rel="">المتصفحات</a> تثبيت تطبيق صفحات الويب التقدميّة PWA والترويج له من داخل واجهات التطبيق مباشرًة، مما يُسهّل عملية تثبيت التطبيق (إضافته إلى الشاشة الرئيسة Add to Home Screen) سواًء على الجوّال أو على سطح المكتب. يؤدي تثبيت التطبيق إلى إضافته للمُشغّل launcher وبالتالي تشغيله مثل أي تطبيق آخر.
</p>

<p>
	يُمكن لمطور التطبيق تخصيص تسلسل خطوات تثبيته مباشرًة داخل التطبيق وذلك إضافًة لسلوك التثبيت المتوفر في المتصفح.
</p>

<p>
	من الأفضل الآخذ بعين الاعتبار لاستخدامات التطبيق PWA قبل التخطيط لترويج تثبيته promote install. فمن أجل التطبيقات التي يُشغلّها المستخدمون عدة مرات في الأسبوع، سيكون من المريح لهم تشغيل التطبيق من الشاشة الرئيسية للجوّال أو من قائمة ابدأ Start menu في نظام تشغيل سطح المكتب. أما بالنسبة لتطبيقات الإنتاج والترفيه، فسيكون من الأنسب تشغيلها في وضع ملء الشاشة بعد إزالة أشرطة <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D9%81%D9%8A-%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-devtools-r277/" rel="">أدوات المتصفح</a> للاستفادة من كامل المساحة الممكنة وذلك باستخدام أحد الوضعين: المستقل <code>standalone</code> أو الحد الأدنى لواجهة المستخدم <code>minimal-ui</code>.
</p>

<p>
	يُبين الشكل التالي زر التثبيت Install App في التطبيق Spotify مثلًا:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="89738" href="https://academy.hsoub.com/uploads/monthly_2022_01/001Spotify.png.2acafa9f55416b30228995271fb68313.png" rel=""><img alt="001Spotify.png" class="ipsImage ipsImage_thumbnailed" data-fileid="89738" data-unique="huo5gbngw" src="https://academy.hsoub.com/uploads/monthly_2022_01/001Spotify.thumb.png.415f9a7a65350038185557346e74f9f8.png" style="width: 300px; height: auto;"></a>
</p>

<h2>
	اقتراح تثبيت تطبيق الويب التقدمي
</h2>

<p>
	لتوضيح إمكانية تثبيت تطبيق PWA وتوفير تسلسل مخصص لخطوات التثبيت من داخل التطبيق:
</p>

<ol>
<li>
		استمع لحدث "قبل طلب التثبيت" <code>beforeinstallprompt</code>.
	</li>
	<li>
		احفظ الحدث <code>beforeinstallprompt</code> لاستخدامه لاحقًا في تشغيل خطوات التثبيت.
	</li>
	<li>
		أعلم المستخدم بإمكانية تثبيت التطبيق ووفر زر أو عنصر آخر لتبدأ منه خطوات التثبيت.
	</li>
</ol>
<p>
	ملاحظة: أُزيل كل من الحدث <code>beforeinstallprompt</code> والحدث <code>appinstalled</code> من توصيف ملف بيان التطبيق manifest file إلى ملف <a href="https://github.com/WICG/manifest-incubations" rel="external nofollow">حضانة بيان الويب</a> (وهو ملف يحوي المواصفات غير المعتمدة لغاية الآن بشكل نهائي). التزم فريق تطوير المتصفح Chrome بدعم هذه الأحداث وعدم إزالتها أو توقيفها، كما يواصل فريق التطوير Web DevRel من Google التوصية باستخدامها لتوفير تجربة تثبيت مخصصة.
</p>

<h3>
	الاستماع لحدث "قبل طلب التثبيت"
</h3>

<p>
	إذا كان التطبيق يحترم معايير التثبيت المطلوبة، فسيشغّل المستعرض حدث "قبل طلب التثبيت" <code>beforeinstallprompt</code>. يجب حفظ مرجع لهذا الحدث وتحديث <a href="https://academy.hsoub.com/design/user-interface/%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-%D9%8A%D8%AC%D8%A8-%D8%AA%D8%B9%D9%84%D9%85%D9%87%D8%A7-%D9%85%D9%86-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%85%D8%A4%D9%84%D9%85%D8%A9-r657/" rel="">واجهة المستخدم</a> لتدل على إمكانية التثبيت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1043_20" style="">
<span class="com">// ‫تهيئة المتغير deferredPrompt للاستخدام اللاحق كي يعرض المتصفح طلب التثبيت للمستخدم</span><span class="pln">
let deferredPrompt</span><span class="pun">;</span><span class="pln">

window</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'beforeinstallprompt'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// منع شريط المعلومات الصغير من الظهور على الجوّال</span><span class="pln">
  e</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">();</span><span class="pln">

  </span><span class="com">// حفظ الحدث لتشغيله لاحقًا</span><span class="pln">
  deferredPrompt </span><span class="pun">=</span><span class="pln"> e</span><span class="pun">;</span><span class="pln">

  </span><span class="com">// تعديل واجهة المستخدم لإعلامه بإمكانية تثبيت التطبيق</span><span class="pln">
  showInstallPromotion</span><span class="pun">();</span><span class="pln">

  </span><span class="com">// يُمكن بشكل خياري تشغيل حدث التحليل لإعلامه بأنه عُرض للمستخدم ترويج للتطبيق</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="str">'beforeinstallprompt'</span><span class="pln"> event was fired</span><span class="pun">.`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	ملاحظة: يوجد العديد من النماذج المختلفة التي يُمكن استخدامها لإعلام المستخدم بإمكانية تثبيت التطبيق وتوفير تسلسل خطوات التثبيت داخل التطبيق. من النماذج الشهيرة وضع زر في <a href="https://academy.hsoub.com/programming/css/%D8%B6%D8%A8%D8%B7-%D9%88%D8%AA%D9%88%D8%B3%D9%8A%D8%B7-%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D8%A7%D9%84%D8%AA%D8%B1%D9%88%D9%8A%D8%B3%D8%A9-header-%D8%B9%D9%85%D9%88%D8%AF%D9%8A%D8%A7-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-css-r115/" rel="">الترويسة header</a> أو عنصر في قائمة التنقل navigation menu أو عنصر في تغذية المحتوى content feed.
</p>

<h3>
	خطوات التثبيت من داخل التطبيق
</h3>

<p>
	يُمكن إتاحة التثبيت من داخل التطبيق بتوفير زر أو أي عنصر واجهة آخر بحيث تبدأ خطوات التثبيت عند النقر عليه، وعندها يجب استدعاء التابع "طلب" <code>prompt</code> على الحدث "قبل طلب التثبيت" <code>beforeinstallprompt</code> المحفوظ في المتغير <code>deferredPrompt</code>، والذي يُظهر مربع حوار منبثق شرطي طالبًا من المستخدم تأكيد رغبته بتثبيت التطبيق.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1043_22" style="">
<span class="pln">buttonInstall</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'click'</span><span class="pun">,</span><span class="pln"> async </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// إخفاء ترويج تثبيت التطبيق</span><span class="pln">
  hideInstallPromotion</span><span class="pun">();</span><span class="pln">
  </span><span class="com">// إظهار طلب التثبيت</span><span class="pln">
  deferredPrompt</span><span class="pun">.</span><span class="pln">prompt</span><span class="pun">();</span><span class="pln">
  </span><span class="com">// انتظار تجاوب المستخدم مع الطلب</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> outcome </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> await deferredPrompt</span><span class="pun">.</span><span class="pln">userChoice</span><span class="pun">;</span><span class="pln">
  </span><span class="com">// إرسال حدث تحليل خيارات المستخدم بشكل اختياري</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">User</span><span class="pln"> response to the install prompt</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">outcome</span><span class="pun">}`);</span><span class="pln">
  </span><span class="com">// إزالة الطلب المُستخدّم إذ لا يُمكن استخدامه ثانيًة</span><span class="pln">
  deferredPrompt </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تُعدّ الخاصية "خيار المستخدم" <code>userChoice</code> بمثابة وعد promise يتحقق حال اختار المستخدم رفض أو قبول التثبيت. يُمكن استدعاء التابع <code>prompt</code> على الحدث المؤجل مرة واحدة، أما إذا رفض المستخدم الطلب فيجب الانتظار حتى تشغيل الحدث <code>beforeinstallprompt</code> مرًة أخرى (بعد تحقيق الوعد <code>userChoice</code> مباشرًة).
</p>

<h2>
	التحقق من تثبيت تطبيق الويب التقدمي
</h2>

<p>
	يُمكن استخدام الوعد "خيار المستخدم" <code>userChoice</code> لتحديد فيما إذا ثبّت المستخدم التطبيق من داخل واجهته، أما إذا كان التثبيت من شريط العنوان أو من أي مكون في المتصفح فلن يُساعد هذا الوعد بأي شيء وعندها يجب الاستماع للحدث <code>appinstalled</code> الذي يُطلق عندما يُثبت التطبيق دون النظر لآلية التثبيت.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1043_24" style="">
<span class="pln">window</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'appinstalled'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// إخفاء ترويج تثبيت التطبيق</span><span class="pln">
  hideInstallPromotion</span><span class="pun">();</span><span class="pln">
  </span><span class="com">// مسح المتغير ليُزال في مرحلة التنظيف</span><span class="pln">
  deferredPrompt </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
  </span><span class="com">// إضافة حدث التحليل للإعلام عن تثبيت ناجح بشكل اختياري</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'PWA was installed'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<h2>
	الكشف عن آلية فتح التطبيق
</h2>

<p>
	يُحدّد <a href="https://academy.hsoub.com/programming/css/%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D8%B3%D8%A7%D8%A6%D8%B7-media-queries-%D9%81%D9%8A-css-r1059/" rel="">استعلام الوسائط</a> لخاصية "نمط العرض" <code>display-mode</code> من الأنماط CSS طريقة فتح التطبيق وذلك إما في تبويب المتصفح أو كتطبيق مُثبّت، وبالتالي يُمكن تطبيق أنماط styles مختلفة وفق كيفية فتح التطبيق. مثلًا: يجب إخفاء زر التثبيت وتوفير زر الرجوع Back عند الفتح كتطبيق منفصل.
</p>

<h3>
	تتبع آلية فتح التطبيق
</h3>

<p>
	يُمكن استخدام التابع <code>matchMedia</code> لاختبار استعلام الوسائط <code>display-mode</code> لتتبع كيفية فتح التطبيق من قبل المستخدمين. بما أن المتصفح Safari على iOS لا يدعم ذلك حتى الآن، فيجب اختبار قيمة الراية <code>navigator.standalone</code> والتي تكون صحيحة إذا كان المتصفح يعمل بوضع "المستقل" standalone.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1043_26" style="">
<span class="kwd">function</span><span class="pln"> getPWADisplayMode</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> isStandalone </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">matchMedia</span><span class="pun">(</span><span class="str">'(display-mode: standalone)'</span><span class="pun">).</span><span class="pln">matches</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">document</span><span class="pun">.</span><span class="pln">referrer</span><span class="pun">.</span><span class="pln">startsWith</span><span class="pun">(</span><span class="str">'android-app://'</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="str">'twa'</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">navigator</span><span class="pun">.</span><span class="pln">standalone </span><span class="pun">||</span><span class="pln"> isStandalone</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="str">'standalone'</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="str">'browser'</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	تتبع تغيير وضع العرض
</h3>

<p>
	يُمكن الاستماع لتغيرات قيمة استعلام الوسائط <code>display-mode</code> لمعرفة فيما إذا انتقل المستخدم من الوضع المستقل <code>standalone</code> إلى وضع تبويب المتصفح <code>browser tab</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1043_28" style="">
<span class="pln">window</span><span class="pun">.</span><span class="pln">matchMedia</span><span class="pun">(</span><span class="str">'(display-mode: standalone)'</span><span class="pun">).</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'change'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">evt</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let displayMode </span><span class="pun">=</span><span class="pln"> </span><span class="str">'browser'</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">evt</span><span class="pun">.</span><span class="pln">matches</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    displayMode </span><span class="pun">=</span><span class="pln"> </span><span class="str">'standalone'</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="com">// إعلام التحليل بتغيير العرض</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'DISPLAY_MODE_CHANGED'</span><span class="pun">,</span><span class="pln"> displayMode</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<h3>
	تحديث واجهة المستخدم وفق نمط العرض الحالي
</h3>

<p>
	يُمكن تطبيق ألوان خلفية مختلفة عند فتح التطبيق كأي تطبيق مثبّت باستخدام تنسيق CSS شرطي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1043_32" style="">
<span class="lit">@media</span><span class="pln"> all and </span><span class="pun">(</span><span class="pln">display</span><span class="pun">-</span><span class="pln">mode</span><span class="pun">:</span><span class="pln"> standalone</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  body </span><span class="pun">{</span><span class="pln">
    background</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> yellow</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	تحديث أيقونة واسم التطبيق
</h2>

<p>
	يُمكن استخدام ملف بيان الويب لتحديث أيقونة واسم التطبيق. للمزيد يُمكن العودة إلى <a href="https://web.dev/manifest-updates/" rel="external nofollow">كيف يعالج المتصفح تحديثات ملف بيان الويب</a>.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://web.dev/customize-install/" rel="external nofollow">How to provide your own in-app install experience</a> للمؤلف Pete LePage.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%85%D9%8A%D8%A9-pwa-r832/" rel="">مدخل إلى تطبيقات الويب التقدمية PWA</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%85%D9%8A%D8%A9-pwa-%D9%81%D9%8A-%D9%88%D8%B6%D8%B9-%D8%A7%D9%86%D9%82%D8%B7%D8%A7%D8%B9-%D8%A7%D9%84%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-r1386/" rel="">تشغيل تطبيقات الويب التقدمية PWA في وضع انقطاع الاتصال</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%B4%D8%B1%D8%AD-%D9%85%D9%84%D9%81-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86-manifest-%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%85%D9%8A-pwa-r1385/" rel="">شرح ملف البيان manifest لتطبيق الويب التقدمي PWA</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%86%D9%85%D8%A7%D8%B0%D8%AC-%D8%A7%D9%82%D8%AA%D8%B1%D8%A7%D8%AD%D8%A7%D8%AA-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%85%D9%8A%D8%A9-pwa-r1446/" rel="">نماذج اقتراحات تثبيت تطبيقات الويب التقدمية PWA</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1445</guid><pubDate>Thu, 20 Jan 2022 16:00:00 +0000</pubDate></item><item><title>&#x643;&#x62A;&#x627;&#x628;&#x629; &#x62A;&#x639;&#x627;&#x628;&#x64A;&#x631; &#x646;&#x645;&#x637;&#x64A;&#x629; RegEx &#x645;&#x62A;&#x642;&#x62F;&#x645;&#x629; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regex-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1421/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_01/61d5487034cdb_---RegEx-.png.5bb962a3308249d68759ba2c50b6d2d9.png" /></p>

<p>
	بعد أن تعرفنا على <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1399/" rel="">أساسيات التعابير النمطية</a> ثم تعرفنا على <a href="http://xn--https-cdhh4euc/academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D8%AC%D9%85%D9%88%D8%B9%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%AC%D8%A7%D9%84%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-r1400/" rel="external nofollow">المجموعات</a> ثم <a href="http://xn--https-cdhh4euc/academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D8%AD%D8%AF%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D9%83%D9%85%D9%8A%D8%A9-%D9%88%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87%D8%A7-%D9%81%D9%8A-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-r1419/" rel="external nofollow">المحددات الكمية</a> ثم <a href="http://xn--https-cdhh4euc/academy.hsoub.com/programming/javascript/%D9%85%D8%B7%D8%A7%D8%A8%D9%82%D8%A9-%D8%B9%D8%AF%D8%A9-%D9%85%D8%AC%D9%85%D9%88%D8%B9%D8%A7%D8%AA-%D9%86%D9%85%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regex-r1420/" rel="external nofollow">تعلمنا كيفية التقاط عدة مجموعات عبر التعابير النمطية</a>، سنقدم فيما يلي مجموعةً من الأفكار المتقدمة في بناء وتنفيذ تعابير نمطية أكثر كفاءةً في البحث عن التطابقات المطلوبة، مثل المراجع References، والمحرف البديل <code>OR</code>، والبحث قُدُمًا lookahead، والبحث إلى الخلف lookbehind، والبحث في موقع محدد باستخدام الراية <code>y</code>.
</p>

<h2>
	المراجع إلى المجموعات بالأعداد والأسماء
</h2>

<p>
	يمكن استخدام محتويات أقواس المجموعات الملتقطة <code>(...)</code> في النتيجة أو في النص البديل، وحتى في النمط نفسه.
</p>

<h3>
	المراجع باستخدام الأعداد
</h3>

<p>
	يمكن الإشارة إلى مجموعة ضمن نمط باستخدام <code>N\</code>، حيث <code>N</code> هو رقم المجموعة، ولتوضيح أهمية هذه الإشارة المرجعية لنتأمل المهمة التالية: نريد إيجاد ما داخل إشارتي التنصيص المفردتين <code>'...'</code> أو المزدوجتين <code>"..."</code> في نص ما، فكيف سننجز ذلك؟
</p>

<p>
	يمكن وضع نوعي إشارة التنصيص في أقواس مربعة <code>['"](?*.)['"]</code>، لكنه في هذه الحالة سيجد محتويات مختلطةً، مثل <code>"...'</code> و<code>'..."</code>، مما سيقودنا إلى تطابقات خاطئة عندما يظهر نوع من الإشارات ضمن آخر، مثل <code>"!She's the one"</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_11" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">He</span><span class="pln"> said</span><span class="pun">:</span><span class="pln"> </span><span class="str">"She's the one!"</span><span class="pun">.`;</span><span class="pln">

let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/['"](.*?)['"]/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

</span><span class="com">// النتيجة ليست كما نتوقع</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// "She'</span></pre>

<p>
	وجد النمط -كما توقعنا- إشارة تنصيص البداية <code>"</code>، ثم استهلك النص بعدها حتى وجد إشارة تنصيص أخرى <code>'</code> أنهت التطابق، وللتأكد من مطابقة إشارة التنصيص الختامية لإشارة تنصيص البداية، يمكن وضعها ضمن قوسي مجموعة، والإشارة إليها بعدد مرجعي <code>1\(?*.)(["'])</code>
</p>

<p>
	إليك الشيفرة الصحيحة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_13" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">He</span><span class="pln"> said</span><span class="pun">:</span><span class="pln"> </span><span class="str">"She's the one!"</span><span class="pun">.`;</span><span class="pln">

let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/(['"])(.*?)\1/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// "She's the one!"</span></pre>

<p>
	سيجد محرك التعبير النمطي إشارة تنصيص البداية <code>(["'])</code>، ويتذكر محتواها الذي يمثل المجموعة الملتقطة الأولى، حيث يعني العدد <code>1\</code> في النمط إيجاد نفس التطابق الموجود في المجموعة الأولى، وهي في حالتنا إشارة تنصيص تطابق تمامًا إشارة البداية.
</p>

<p>
	وسيعني العدد <code>2\</code> محتويات المجموعة الثانية، والعدد <code>3\</code> محتويات الثالثة، وهكذا.
</p>

<p>
	لاحظ، لن نتمكن من الإشارة إلى مجموعة إذا استخدمنا نمط الاستثناء <code>:?</code>، ولن يتذكر المحرك محتوى المجموعات المستثناة <code>(...:?)</code>.
</p>

<p>
	انتبه، لا تخلط بين الإشارة <code>1\</code> في نمط ما، والإشارة <code>1$</code> في النص البديل، حيث نستخدم إشارة الدولار <code>1$</code> في الإشارة المرجعية ضن النص البديل، والشرطة المعكوسة <code>1\</code> ضمن الأنماط.
</p>

<h3>
	المراجع باستخدام الأسماء
</h3>

<p>
	إذا احتوى التعبير النمطي على عدة أقواس، فمن الأنسب استخدام الأسماء للدلالة عليها، نستخدم <code>&lt;k&lt;name\</code> في الدلالة على القوس بالاسم، فإذا سُميت مجموعة ضمن التعبير النمطي بالاسم <code>&lt;quote&gt;?</code>، فسيكون الاسم المرجعي لها هو <code>&lt;k&lt;quote\</code>، وإليك مثالًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_15" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">He</span><span class="pln"> said</span><span class="pun">:</span><span class="pln"> </span><span class="str">"She's the one!"</span><span class="pun">.`;</span><span class="pln">

let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/(?&lt;quote&gt;['"])(.*?)\k&lt;quote&gt;/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// "She's the one!"</span></pre>

<h2>
	البديل باستخدام OR في التعابير النمطية
</h2>

<p>
	يعني مصطلح "البديل" Alternation في التعابير النمطية استخدام العملية المنطقية "OR"، ويرمز لها ضمن التعابير النمطية بالخط العمودي <code>|</code>، فلو أردنا مثلًا إيجاد لغات برمجة مثل <a href="wiki.hsoub.com/HTML" rel="">HTML</a> أو <a href="wiki.hsoub.com/PHP" rel="">PHP</a> أو Java أو <a href="wiki.hsoub.com/JavaScript" rel="">JavaScript</a>، فسيكون التعبير النمطي المناسب هو <code>?(html|php|java(script</code>، وإليك مثالًا تطبيقيًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_17" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/html|php|css|java(script)?/</span><span class="pln">gi</span><span class="pun">;</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"First HTML appeared, then CSS, then JavaScript"</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 'HTML', 'CSS', 'JavaScript'</span></pre>

<p>
	لكننا رأينا سابقًا أنّ الأقواس المربعة تنفذ أمرًا مشابهًا، فهي تسمح باختيار أحد المحارف التي توضع ضمنها، فإذا استخدمنا النمط <code>gr[ae]y</code> مثلًا فسنحصل على التطابقين <code>gray</code> أو <code>grey</code>، إذ تسمح الأقواس المربعة باستخدام المحارف أو أصناف المحارف ضمنها، بينما يسمح البديل باستخدام أي عبارات، حيث يعني التعبير <code>A|B|C</code> أيًا من العبارات A أو B أو C، وإليك بعض الأمثلة:
</p>

<ul>
<li>
		يماثل النمط <code>gr(a|e)y</code> النمط <code>gr[ae]y</code>.
	</li>
	<li>
		يعني النمط <code>gra|ey</code> أيًا من gra أو ey.
	</li>
</ul>
<p>
	ولتطبيق البديل على جزء محدد من نمط، يمكن وضع هذا الجزء داخل قوسين:
</p>

<ul>
<li>
		يطابق النمط <code>I love HTML|CSS</code> كلًا من I love HTML أو <a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a>.
	</li>
	<li>
		يطابق النمط <code>(I love (HTML|CSS</code> كلًا من I love HTML أو I love CSS
	</li>
</ul>
<h3>
	مثال: تعبير نمطي لإيجاد الوقت
</h3>

<p>
	صادفنا في المقالات السابقة مهمة بناء تعبير نمطي يبحث عن الوقت وفق التنسيق <code>hh:mm</code>، مثل <code>12:00</code>، لكن التعبير الذي استُخدم <code>d\d:\d\d\</code> سطحي جدًا، إذ يقبل هذا النمط قيمًا خاطئة للتوقيت مثل <code>25:99</code>، فكيف سننجز نمطًا أفضل؟
</p>

<p>
	بالنسبة للساعات:
</p>

<ul>
<li>
		إذا كان الرقم الأول <code>0</code> أو<code>1</code> فيمكن أن يكون الثاني أي رقم.
	</li>
	<li>
		إذا كان الرقم الأول <code>2</code>، فيجب أن يكون الثاني <code>[‎0-3]</code>.
	</li>
	<li>
		لا يسمح بأي رقم آخر غير ذلك.
	</li>
</ul>
<p>
	يمكن كتابة تعبير نمطي يضم الحالتين باستخدام البديل بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_20" style="">
<span class="pun">[</span><span class="lit">01</span><span class="pun">]</span><span class="pln">\d</span><span class="pun">|</span><span class="lit">2</span><span class="pun">[</span><span class="lit">0</span><span class="pun">-</span><span class="lit">3</span><span class="pun">]</span></pre>

<p>
	أما بالنسبة للدقائق: ينبغي أن تكون الدقائق بين <code>00</code> و<code>59</code>، ويكتب هذا في التعبير النمطي بالشكل <code>‎[0-5]\d</code>، أي يمكن أن تكون الآحاد أي رقم، والعشرات من 1 إلى 5.
</p>

<p>
	وعند ضم الساعات والدقائق معًا سنحصل على التعبير:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_22" style="">
<span class="pun">[</span><span class="lit">01</span><span class="pun">]</span><span class="pln">\d</span><span class="pun">|</span><span class="lit">2</span><span class="pun">[</span><span class="lit">0</span><span class="pun">-</span><span class="lit">3</span><span class="pun">]:[</span><span class="lit">0</span><span class="pun">-</span><span class="lit">5</span><span class="pun">]</span><span class="pln">\d</span></pre>

<p>
	انتهينا تقريبًا لكن مع وجود مشكلة صغيرة، إذ وضعنا محرف البديل <code>|</code> بالشكل السابق فصل التعبير إلى قسمين منفصلين:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_24" style="">
<span class="pun">[</span><span class="lit">01</span><span class="pun">]</span><span class="pln">\d    </span><span class="pun">|</span><span class="pln">    </span><span class="lit">2</span><span class="pun">[</span><span class="lit">0</span><span class="pun">-</span><span class="lit">3</span><span class="pun">]:[</span><span class="lit">0</span><span class="pun">-</span><span class="lit">5</span><span class="pun">]</span><span class="pln">\d</span></pre>

<p>
	إليك ما سيحدث، سيبحث النمط عن أحد النمطين، لكن هذا خاطئ، لأنّ البديل سيستخدَم فقط في قسم الساعات من التعبير النمطي، وسنصلح ذلك بوضع قسم الساعات ضمن قوسين
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_28" style="">
<span class="pun">([</span><span class="lit">01</span><span class="pun">]</span><span class="pln">\d</span><span class="pun">|</span><span class="lit">2</span><span class="pun">[</span><span class="lit">0</span><span class="pun">-</span><span class="lit">3</span><span class="pun">]):[</span><span class="lit">0</span><span class="pun">-</span><span class="lit">5</span><span class="pun">]</span><span class="pln">\d</span></pre>

<p>
	إليك الحل النهائي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_30" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/([01]\d|2[0-3]):[0-5]\d/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="str">"00:00 10:10 23:59 25:99 1:2"</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">));</span><span class="pln"> </span><span class="com">// 00:00,10:10,23:59</span></pre>

<h2>
	التحقق مما يلي أو يسبق التعبير النمطي
</h2>

<p>
	نحتاج في بعض الأحيان إلى إيجاد تطابقات بشرط أن يأتي بعدها أو قبلها تطابقًا أو نمطًا محددًا دون أن تدخل تلك التطابقات ضمن قيم النتيجة النهائية، ولهذه الغاية سنجد صيغتا تحقق تُدعيان "انظر أمام النمط" lookahead،" وانظر خلف النمط "lookbehind،" وقبل أن نبدأ موضوعنا، سنأخذ مثالًا نحاول فيه إيجاد السعر ضمن النص <code>‎1 turkey costs 30€</code>، وهو عدد تتبعه الإشارة <code>€</code>.
</p>

<h3>
	انظر أمام التعبير النمطي
</h3>

<p>
	النمط <code>(X(?=Y</code> يعني "ابحث عن <code>X</code>، لكن أعد نتيجة التطابق فقط إذا كان بعدها <code>Y</code>"، ويمكن أن تجد أي نمط مكان <code>X</code> أو <code>Y</code>، فمن أجل عدد صحيح تتبعه الإشارة <code>€</code>، سيكون النمط المناسب هو <code>(€=?)+d\</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_32" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"1 turkey costs 30€"</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">/\d+(?=€)/</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 30, € يهمل الرقم 1 لأنه غير متبوع بالإشارة</span></pre>

<p>
	لاحظ أنّ مطابقة ما أمام النمط هو اختبار وعملية تحقيق فقط ببساطة، ولا يشكل محتوى ما بين القوسين جزءًا من النتيجة، فعندما نبحث عن النمط <code>(X(?=Y</code>، فسيجد المحرك <code>X</code> ومن ثم يتحقق من وجود <code>Y</code> بعدها مباشرةً إن وجده أعاد X فقط وإلا لم يعد شيئًا، ثم يتابع البحث.
</p>

<p>
	يمكن كتابة اختبارات أكثر تعقيدًا، مثل النمط <code>(x(?=y)(?=z</code> الذي يعني:
</p>

<ol>
<li>
		جد <code>X</code>.
	</li>
	<li>
		تحقق من وجود <code>Y</code> مباشرةً بعد <code>X</code>، وتجاوز التطابق إن لم يتحقق ذلك.
	</li>
	<li>
		تحقق أيضًا من وجود <code>Z</code> مباشرةً بعد <code>X</code>، وتجاوز التطابق إن لم يتحقق ذلك.
	</li>
	<li>
		إذا نجح الاختباران السابقان فأعد قيمة <code>X</code>، وإلا فتابع البحث.
	</li>
</ol>
<p>
	أي أننا نبحث عن <code>X</code> التي تتبعها القيمتان <code>Y</code> و<code>Z</code> في نفس الوقت.
</p>

<p>
	وسيبحث النمط <code>(d+(?=\s)(?=.*30\</code> عن عدد <code>+d\</code> يتبعه فراغ <code>(s\=?)</code>، ثم العدد <code>30</code> في مكان ما من النص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_34" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"1 turkey costs 30€"</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">/\d+(?=\s)(?=.*30)/</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span></pre>

<p>
	التطابق الوحيد المناسب في المثال السابق هو <code>1</code>.
</p>

<p>
	لنفترض بأننا سنحتاج إلى الكمية وليس السعر من النص نفسه، وبالتالي سنحتاج إلى عدد <code>+d\</code> غير متبوع بالإشارة <code>€</code>، يمكن إنجاز ذلك باستعمال النمط الاختباري <code>(x(?!y</code>، أي "انظر أمام النمط، يجب أن لا يتحقق الشرط" بمعنى ابحث عن <code>X</code> التي لا يتبعها <code>Y</code> مباشرةً.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_36" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"2 turkeys cost 60€"</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">/\d+\b(?!€)/</span><span class="pln">g</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 2 (the price is not matched)</span></pre>

<h3>
	انظر خلف التعبير النمطي
</h3>

<p>
	يشابه هذا النمط نمط التحقق مما يلي التعبير، لكنه يضع شرطًا على ما قبل التعبير المتطابق ويمكن أن نستخدمه بالصيغ التالية:
</p>

<ul>
<li>
		"التحقق من مطابقة ما يسبق التعبير" Positive lookbehind عبر الصيغة <code>Y)X=&gt;?)</code>: أوجد <code>X</code> إذا سبقتها <code>Y</code>.
	</li>
	<li>
		"التحقق من عدم مطابقة ما يسبق التعبير" Negative lookbehind عبر الصيغة <code>Y)X!&gt;?)</code>: أوجد <code>X</code> إذا لم تسبقها <code>Y</code>.
	</li>
</ul>
<p>
	لنغير على سبيل المثال السعر إلى دولار، حيث تكون إشارة الدولار <code>$</code> عادةً قبل السعر، فللبحث عن <code>30$</code> سنستخدم النمط <code>+d\($\=&gt;?)</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_38" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"1 turkey costs $30"</span><span class="pun">;</span><span class="pln">

</span><span class="com">//  \$ سنتجاوز الإشارة</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">/(?&lt;=\$)\d+/</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 30 </span></pre>

<p>
	وإذا أردنا الكمية، فسنبحث عن عدد لا تسبقه الإشارة $، وبالتالي نستخدم البحث السلبي خلفًا <code>+d\($\!&gt;?)</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_40" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"2 turkeys cost $60"</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">/(?&lt;!\$)\b\d+/</span><span class="pln">g</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 2 </span></pre>

<h3>
	إدخال الشرط ضمن نتيجة التطابق
</h3>

<p>
	لا يظهر محتوى ما بين القوسين جزءًا من النتيجة عادةً، فلن تكون الإشارة <code>€</code> مثلًا ضمن نتيجة التطابق مع النمط <code>(€=?)+d\</code>. وهذا أمر طبيعي، لأننا نبحث عن العدد الذي قبلها، وهي مجرد اختبار، لكن قد نحتاج في بعض الحالات إلى ما حول التطابق أو إلى جزء منه، ويمكن إنجاز ذلك بوضع الجزء المطلوب ضمن قوسين إضافيين، سنعيد في المثال التالي السعر مع إشارة العملة <code>(kr|€)</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_42" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"1 turkey costs 30€"</span><span class="pun">;</span><span class="pln">
let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/\d+(?=(€|kr))/</span><span class="pun">;</span><span class="pln"> </span><span class="com">//  €|kr أقواس إضافية حول </span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 30, €</span></pre>

<p>
	إليك مثالًا مع البحث خلفًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_44" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"1 turkey costs $30"</span><span class="pun">;</span><span class="pln">
let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/(?&lt;=(\$|£))\d+/</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 30, $</span></pre>

<h2>
	الراية y والبحث في موقع محدد
</h2>

<p>
	تسمح الراية <code>y</code> بالبحث في موقع محدد ضمن النص الأصلي، ولفهم عمل هذه الراية وأساليب استخدامها في التعابير النمطية جيدًا سنطرح مشكلةً عمليةً، حيث تعتبر عملية التحليل التجريدي لنص lexical analysis من المهام الأساسية لمحركات التعابير النمطية، ففي نصوص الشيفرة في لغات البرمجة تلعب محركات التعابير الدور الأساسي في إيجاد العناصر البنيوية، مثل وسوم <a href="http://wiki.hsoub.com/HTML" rel="external">HTML</a> والسمات التي في داخلها، وستساعد في <a href="https://wiki.hsoub.com/JavaScript" rel="external">JavaScript</a> على إيجاد المتغيرات والدوال وغيرها، ولن ندخل في موضوع إنشاء المحللات التجريدية فهي خارج نطاق موضوعنا، لكن توجد مهمة مشتركة وهي إيجاد شيء ما في موقع محدد، لنفترض مثلًا أنّ لدينا الشيفرة التالية <code>"let varName = "value</code> في صيغة نص، ونحتاج إلى تحديد اسم المتغير الذي سيبدأ في الموقع الرابع، سنبحث عن اسم المتغير من خلال التعبير <code>+w\</code>، وطبعًا نحتاج إلى تعابير أكثر تعقيدًا للبحث الدقيق عن أسماء المتغيرات في JavaScript لكن لن نهتم بذلك حاليًا.
</p>

<ul>
<li>
		سيجد التابع <code>(/+str.match(/\w</code> الكلمة الأولى فقط في السطر وهي (<code>let</code>)، وطبعًا ليست هي الكلمة المطلوبة.
	</li>
	<li>
		ولو استخدمنا الراية <code>g</code> فسيبحث التابع عن جميع الكلمات في النص، لكننا لا نحتاج سوى الكلمة الموجودة في الموقع الرابع.
	</li>
</ul>
<p>
	<strong>كيف سنبحث عن نمط محدد ابتداءً من موقع محدد؟</strong> لنحاول استخدام التابع <code>(regexp.exec(str</code>، حيث سيبحث هذا التابع عن التطابق الأول بطريقة مشابهة للتابع <code>(str.match(regexp</code> عندما يُستخدم دون الرايتين <code>g</code> أو <code>y</code>، لكنه سيبحث في النص ابتداءً من الموقع المخزن في الخاصية <code>regexp.lastIndex</code> عندما نفعّل الراية <code>g</code>، فإذا وجد تطابقًا فسيخزن الموقع الذي يلي التطابق في الخاصية <code>regexp.lastIndex</code>، أي ستكون الخاصية <code>regexp.lastIndex</code> نقطة بداية للبحث، وسيغير كل استدعاء للتابع <code>(regexp.exec(str</code> قيمتها إلى القيمة الجديدة -بعد إيجاد تطابق-، وبالتالي سيعيد الاستدعاء المتكرر للتابع <code>(regexp.exec(str</code> التطابقات واحدًا تلو الآخر، وإليك مثالًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_46" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">'let varName'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// لنبحث عن كل الكلمات في هذه السلسلة</span><span class="pln">
let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/\w+/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">.</span><span class="pln">lastIndex</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 0 ( lastIndex=0)</span><span class="pln">

let word1 </span><span class="pun">=</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="pln">str</span><span class="pun">);</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">word1</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln"> </span><span class="com">// let (الكلمة الأولى)</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">.</span><span class="pln">lastIndex</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 3 (الموقع بعد التطابق)</span><span class="pln">

let word2 </span><span class="pun">=</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="pln">str</span><span class="pun">);</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">word2</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln"> </span><span class="com">// varName (الكلمة الثانية)</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">.</span><span class="pln">lastIndex</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 11 (الموقع بعد التطابق)</span><span class="pln">

let word3 </span><span class="pun">=</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="pln">str</span><span class="pun">);</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">word3</span><span class="pun">);</span><span class="pln"> </span><span class="com">// null (لا تطابقات)</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">.</span><span class="pln">lastIndex</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 0 (يصفر قيمة الخاصية)</span></pre>

<p>
	ويمكن الحصول على كل التطابقات من خلال حلقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_49" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">'let varName'</span><span class="pun">;</span><span class="pln">
let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/\w+/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

let result</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result </span><span class="pun">=</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="pln">str</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Found</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">result</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]}</span><span class="pln"> at position $</span><span class="pun">{</span><span class="pln">result</span><span class="pun">.</span><span class="pln">index</span><span class="pun">}`</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
  </span><span class="com">// Found let at position 0, then</span><span class="pln">
  </span><span class="com">// Found varName at position 4</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمثل استخدام التابع <code>regexp.exec</code> أسلوبًا بديلًا عن <code>str.matchAll</code> مع بعض السيطرة على مجرى العملية.
</p>

<p>
	بالعودة إلى مهمتنا، سنتمكن من تحديد الموقع بإسناد القيمة 4 إلى الخاصية <code>lastIndex</code> لنبدأ البحث من الموقع المطلوب بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_51" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">'let varName = "value"'</span><span class="pun">;</span><span class="pln">

let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/\w+/</span><span class="pln">g</span><span class="pun">;</span><span class="pln"> </span><span class="com">// "g" دون استخدام lastIndex يتجاهل المحرك الخاصية</span><span class="pln">

regexp</span><span class="pun">.</span><span class="pln">lastIndex </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4</span><span class="pun">;</span><span class="pln">

let word </span><span class="pun">=</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="pln">str</span><span class="pun">);</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">word</span><span class="pun">);</span><span class="pln"> </span><span class="com">// varName</span></pre>

<p>
	لقد حُلِّت المشكلة! والنتيجة صحيحة، لكن ينبغي أن نتروّى قليلًا..
</p>

<p>
	يبدأ التابع <code>regexp.exec</code> البحث انطلاقًا من الموقع <code>lastIndex</code>، ثم يستأنف البحث، فإذا لم يكن التطابق موجودًا في هذا الموقع بل في موقع يأتي بعده فسيجده أيضًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_53" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">'let varName = "value"'</span><span class="pun">;</span><span class="pln">

let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/\w+/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

</span><span class="com">// ابدأ البحث في الموقع 3</span><span class="pln">
regexp</span><span class="pun">.</span><span class="pln">lastIndex </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">

let word </span><span class="pun">=</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="pln">str</span><span class="pun">);</span><span class="pln">
</span><span class="com">// سيجد التطابق في الموقع 4</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">word</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln"> </span><span class="com">// varName</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">word</span><span class="pun">.</span><span class="pln">index</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 4</span></pre>

<p>
	إنّ هذا الأمر خاطئ في العديد من المهام ومنها التحليل التجريدي، إذ يجب إيجاد التطابق في الموقع المحدد تمامًا وليس بعده، وهذا سبب وجود الراية <code>y</code>. حيث تجبر الراية <code>y</code> التابع <code>regexp.exec</code> على البحث في الموقع <code>lastIndex</code> بالتحديد وليس انطلاقًا منه.
</p>

<p>
	إليك مثالًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_55" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">'let varName = "value"'</span><span class="pun">;</span><span class="pln">

let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/\w+/</span><span class="pln">y</span><span class="pun">;</span><span class="pln">

regexp</span><span class="pun">.</span><span class="pln">lastIndex </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// null (يوجد فراغ في هذا الموقع وليس كلمة)</span><span class="pln">

regexp</span><span class="pun">.</span><span class="pln">lastIndex </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4</span><span class="pun">;</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> regexp</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="pln">str</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// varName (الكلمة في الموقع 4)</span></pre>

<p>
	لاحظ أننا لن نحصل على التطابق مع التعبير <code>w+/y\/</code> في الموقع <code>3</code> (على عكس <code>g</code>) لكن في الموقع <code>4</code>، وليس هذا فحسب، بل توجد فائدة أخرى لاستخدام الراية <code>y</code>، تخيل أن لدينا نصًا طويلًا ولا توجد فيه أية تطابقات للنمط الذي نبحث عنه، سيبحث المحرك عند تفعيل الراية <code>g</code> حتى نهاية النص ولن يجد نتيجةً، وهذا ما سيستهلك الوقت مقارنةً باستخدام الراية <code>y</code> التي تتحقق فقط في الموقع المحدد.
</p>

<p>
	سنواجه في المهام التي تشبه التحليل التجريدي عمليات بحث متعددةً في مواقع محددة، للتحقق مما هو موجود، وسيكون استخدام الراية <code>y</code> المفتاح لإنجاز مثل هذه العمليات بدقة وفعالية.
</p>

<h2>
	خلاصة
</h2>

<ul>
<li>
		يفيدنا التحقق مما يلي التطابق ويسبقه في إيجاد تطابق بناءً على ما قبله أو بعده.
	</li>
	<li>
		يمكن استخدام تعابير نمطية بسيطة لإنجاز ذلك يدويًا، كأن نبحث عن كل التطابقات المطلوبة ونرشحها من خلال الحلقات وفق السياق المطلوب.
	</li>
	<li>
		تذكر أن التابع <code>str.match</code> (مع الراية g) والتابع <code>str.match</code> (دائمًا) سيعيدان مصفوفةً لها الخاصية <code>index</code>، وبالتالي سنعلم تمامًا موقع التطابق في النص، ثم نتحقق مما قبله أو بعده.
	</li>
	<li>
		إذا نفِّذ البحث خلفًا أو قدمًا آليًا فسيكون أفضل من الطريقة اليدوية، مثلما فعلنا في هذا المقال:
	</li>
</ul>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th style="text-align:left">
				النمط
			</th>
			<th style="text-align:left">
				نوع البحث
			</th>
			<th style="text-align:left">
				ماذا يُطابق؟
			</th>
		</tr></thead>
<tbody>
<tr>
<td style="text-align:left">
				<code>(X(?=Y</code>
			</td>
			<td style="text-align:left">
				بحث إيجابي قُدمًا
			</td>
			<td style="text-align:left">
				<code>X</code> إذا جاءت بعدها <code>Y</code>
			</td>
		</tr>
<tr>
<td style="text-align:left">
				<code>(X(?!Y</code>
			</td>
			<td style="text-align:left">
				بحث سلبي قدمًا
			</td>
			<td style="text-align:left">
				<code>X</code> إذا لم تكن بعدها <code>Y</code>
			</td>
		</tr>
<tr>
<td style="text-align:left">
				<code>y)x=&gt;?)</code>
			</td>
			<td style="text-align:left">
				بحث إيجابي خلفًا
			</td>
			<td style="text-align:left">
				<code>X</code> إذا جاءت بعد <code>Y</code>
			</td>
		</tr>
<tr>
<td style="text-align:left">
				<code>y)x!&gt;?)</code>
			</td>
			<td style="text-align:left">
				بحث سلبي خلفًا
			</td>
			<td style="text-align:left">
				<code>X</code> إذا لم تأت بعد <code>Y</code>
			</td>
		</tr>
</tbody>
</table>
<ul>
<li>
		تجبر الراية <code>y</code> التابع <code>regexp.exec</code> على البحث في الموقع <code>lastIndex</code> بالتحديد وليس انطلاقًا منه.
	</li>
</ul>
<h2>
	مهام لإنجازها
</h2>

<h3>
	إيجاد لغات البرمجة
</h3>

<p>
	اكتب تعبيرًا نمطيًا يبحث عن أسماء لغات البرمجة الموجودة في النص <code>Java JavaScript PHP C++ C</code>
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_57" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/your regexp/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="str">"Java JavaScript PHP C++ C"</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">));</span><span class="pln"> </span><span class="com">// Java JavaScript PHP C++ C</span></pre>

<p>
	<strong>الحل</strong>: تقتضي الفكرة الأولى بترتيب اللغات بحيث يفصل بينها المحرف<code>|</code>، لكن هذا الحل لن ينفع:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_59" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/Java|JavaScript|PHP|C|C\+\+/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Java, JavaScript, PHP, C, C++"</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// Java,Java,PHP,C,C</span></pre>

<p>
	سيبحث محرك <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1374/" rel="">التعابير النمطية</a> عن البدائل واحدًا تلو الآخر، فلو تحقق بداية من "Java" ووجدها، فلن يجد بالطبع "JavaScript" وكذلك الأمر بالنسبة للغتين "C" و "++C".
</p>

<p>
	هنالك حلين للمشكلة:
</p>

<ul>
<li>
		غير ترتيب اللغات لتكون اللغة ذات الاسم الأطول أولًا:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_61" style="">
<span class="typ">JavaScript</span><span class="pun">|</span><span class="typ">Java</span><span class="pun">|</span><span class="pln">C\+\+</span><span class="pun">|</span><span class="pln">C</span><span class="pun">|</span><span class="pln">PHP</span></pre>

<ul>
<li>
		ادمج اللغات التي لها نفس البداية :
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_63" style="">
<span class="pln"> </span><span class="typ">Java</span><span class="pun">(</span><span class="typ">Script</span><span class="pun">)?|</span><span class="pln">C</span><span class="pun">(</span><span class="pln">\+\+</span><span class="pun">)?|</span><span class="pln">PHP</span></pre>

<p>
	إليك الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_66" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/Java(Script)?|C(\+\+)?|PHP/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Java, JavaScript, PHP, C, C++"</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// Java,JavaScript,PHP,C,C++</span></pre>

<h3>
	إيجاد زوجي وسوم [tag]…[/tag]
</h3>

<p>
	لنفترض وجود زوج الوسوم <code>[tag]...[/tag]</code>، حيث يمثّل <code>tag</code> أحد العناصر <code>b</code> أو <code>url</code> أو <code>quote</code>، إليك مثالًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_68" style="">
<span class="pun">[</span><span class="pln">b</span><span class="pun">]</span><span class="pln">text</span><span class="pun">[/</span><span class="pln">b</span><span class="pun">]</span><span class="pln">
</span><span class="pun">[</span><span class="pln">url</span><span class="pun">]</span><span class="pln">http</span><span class="pun">:</span><span class="com">//google.com[/url]</span></pre>

<p>
	قد تكون هذه الوسوم متداخلةً، لكن لا يمكن أن يتداخل العنصر مع نفسه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_70" style="">
<span class="typ">Normal</span><span class="pun">:</span><span class="pln">
</span><span class="pun">[</span><span class="pln">url</span><span class="pun">]</span><span class="pln"> </span><span class="pun">[</span><span class="pln">b</span><span class="pun">]</span><span class="pln">http</span><span class="pun">:</span><span class="com">//google.com[/b] [/url]</span><span class="pln">
</span><span class="pun">[</span><span class="pln">quote</span><span class="pun">]</span><span class="pln"> </span><span class="pun">[</span><span class="pln">b</span><span class="pun">]</span><span class="pln">text</span><span class="pun">[/</span><span class="pln">b</span><span class="pun">]</span><span class="pln"> </span><span class="pun">[/</span><span class="pln">quote</span><span class="pun">]</span><span class="pln">

</span><span class="typ">Can</span><span class="str">'</span><span class="pln">t happen</span><span class="pun">:</span><span class="pln">
</span><span class="pun">[</span><span class="pln">b</span><span class="pun">][</span><span class="pln">b</span><span class="pun">]</span><span class="pln">text</span><span class="pun">[/</span><span class="pln">b</span><span class="pun">][/</span><span class="pln">b</span><span class="pun">]</span></pre>

<p>
	ليس بالضرورة أن تكون وسوم البداية والنهاية لعنصر على نفس السطر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_74" style="">
<span class="pun">[</span><span class="pln">quote</span><span class="pun">]</span><span class="pln">
  </span><span class="pun">[</span><span class="pln">b</span><span class="pun">]</span><span class="pln">text</span><span class="pun">[/</span><span class="pln">b</span><span class="pun">]</span><span class="pln">
</span><span class="pun">[/</span><span class="pln">quote</span><span class="pun">]</span></pre>

<p>
	اكتب تعبيرًا نمطيًا للبحث عن تلك الوسوم ومحتوياتها، إليك مثالًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_76" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/your regexp/</span><span class="pln">flags</span><span class="pun">;</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"..[url]http://google.com[/url].."</span><span class="pun">;</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// [url]http://google.com[/url]</span></pre>

<p>
	إذا كانت الوسوم متداخلةً فنريد فقط العنصر الخارجي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_79" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/your regexp/</span><span class="pln">flags</span><span class="pun">;</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"..[url][b]http://google.com[/b][/url].."</span><span class="pun">;</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// [url][b]http://google.com[/b][/url]</span></pre>

<p>
	<strong>الحل</strong>: يُعطى التعبير النمطي للبحث عن وسم البداية بالشكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_81" style="">
<span class="pln">\[</span><span class="pun">(</span><span class="pln">b</span><span class="pun">|</span><span class="pln">url</span><span class="pun">|</span><span class="pln">quote</span><span class="pun">)]</span></pre>

<p>
	ولإيجاد المحتوى كاملًا حتى بلوغ وسم النهاية سنستخدم النمط <code>?*</code> مع الراية <code>s</code> لالتقاط أي محرف بما في ذلك محرف بداية السطر ثم سنضيف مرجعًا للخلف إلى وسم النهاية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_84" style="">
<span class="pln">\[</span><span class="pun">(</span><span class="pln">b</span><span class="pun">|</span><span class="pln">url</span><span class="pun">|</span><span class="pln">quote</span><span class="pun">)</span><span class="pln">\]</span><span class="pun">.*?</span><span class="pln">\[</span><span class="pun">/</span><span class="pln">\1</span><span class="pun">]</span></pre>

<p>
	إليك الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_86" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/\[(b|url|quote)].*?\[\/\1]/</span><span class="pln">gs</span><span class="pun">;</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">
  </span><span class="pun">[</span><span class="pln">b</span><span class="pun">]</span><span class="pln">hello</span><span class="pun">![/</span><span class="pln">b</span><span class="pun">]</span><span class="pln">
  </span><span class="pun">[</span><span class="pln">quote</span><span class="pun">]</span><span class="pln">
    </span><span class="pun">[</span><span class="pln">url</span><span class="pun">]</span><span class="pln">http</span><span class="pun">:</span><span class="com">//google.com[/url]</span><span class="pln">
  </span><span class="pun">[/</span><span class="pln">quote</span><span class="pun">]</span><span class="pln">
</span><span class="pun">`;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// [b]hello![/b],[quote][url]http://google.com[/ur</span></pre>

<h3>
	إيجاد ما بين إشارتي التنصيص "…."
</h3>

<p>
	أنشئ تعبيرًا نمطيًا لإيجاد سلسلة نصية بين إشارتي تنصيص"….".
</p>

<p>
	يجب أن يدعم النص فكرة تجاوز المحارف تمامًا كما في جافا سكربت. أي يمكن أن نضع علامة التنصيص بالشكل <code>"\</code> وأن نضع علامة السطر الجديد بالشكل <code>n\</code> والشرطة المائلة بالشكل <code>\\</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_88" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Just like \"here\"."</span><span class="pun">;</span></pre>

<p>
	وانتبه إلى أن علامة التنصيص التي يجري تجاوزها <code>"\</code> لن تعتبر نهاية للسلسلة النصية. وبالتالي لا بدّ من البحث من إشارة تنصيص إلى أخرى متجاهلين علامات التنصيص التي يجري تجاوزها.
</p>

<p>
	أمثلة عن نصوص وتطابقاتها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_90" style="">
<span class="pun">..</span><span class="pln"> </span><span class="str">"test me"</span><span class="pln"> </span><span class="pun">..</span><span class="pln">
</span><span class="pun">..</span><span class="pln"> </span><span class="str">"Say \"Hello\"!"</span><span class="pln"> </span><span class="pun">...</span><span class="pln"> </span><span class="pun">(داخلها</span><span class="pln"> </span><span class="pun">علامة</span><span class="pln"> </span><span class="pun">تنصيص</span><span class="pln"> </span><span class="pun">جرى</span><span class="pln"> </span><span class="pun">تجاوزها)</span><span class="pln">
</span><span class="pun">..</span><span class="pln"> </span><span class="str">"\\"</span><span class="pln"> </span><span class="pun">..</span><span class="pln">  </span><span class="pun">(داخلها</span><span class="pln"> </span><span class="pun">شرطة</span><span class="pln"> </span><span class="pun">مائلة</span><span class="pln"> </span><span class="pun">مزدوجة)</span><span class="pln">
</span><span class="pun">..</span><span class="pln"> </span><span class="str">"\\ \""</span><span class="pln"> </span><span class="pun">..</span><span class="pln">  </span><span class="pun">(داخلها</span><span class="pln"> </span><span class="pun">شرطة</span><span class="pln"> </span><span class="pun">مائلة</span><span class="pln"> </span><span class="pun">مزدوجة</span><span class="pln"> </span><span class="pun">وعلامة</span><span class="pln"> </span><span class="pun">تنصيص</span><span class="pln"> </span><span class="pun">جرى</span><span class="pln"> </span><span class="pun">تجاوزها)</span></pre>

<p>
	لا بد في جافاسكربت من مضاعفة الشرطات المائلة لتمريرها إلى النصوص كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_92" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">' .. "test me" .. "Say \\"Hello\\"!" .. "\\\\ \\"" .. '</span><span class="pun">;</span><span class="pln">

</span><span class="com">// the in-memory string</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">str</span><span class="pun">);</span><span class="pln"> </span><span class="com">//  .. "test me" .. "Say \"Hello\"!" .. "\\ \"" ..</span></pre>

<p>
	<strong>الحل</strong>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_94" style="">
<span class="str">/"(\\.|[^"\\])*"/</span><span class="pln">g</span></pre>

<p>
	نتبع الآتي خطوةً بخطوة:
</p>

<ul>
<li>
		نبحث أولًا عن إشارة تنصيص البداية <code>"</code>.
	</li>
	<li>
		إن كان هناك شرطة عكسية <code>\\</code> لا بد من مضاعفتها في التعبير لأنها محرف خاص. بعدها يمكن أن يأتي أي محرف.
	</li>
	<li>
		باستثناء الحالتين السابقتين يمكن أن نأخذ أية محارف ما عدا إشارة التنصيص (التي تعني نهاية النص) والشرطة العكسية (تستخدم الشرطة العكسية فقط مع رموز أخرى بعدها لمنع ظهورها مفردة): <code>[^"\\]</code>
	</li>
	<li>
		وهكذا حتى نصل إلى إشارة تنصيص نهاية النص.
	</li>
</ul>
<p>
	إليك الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_96" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/"(\\.|[^"\\])*"/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">
let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">' .. "test me" .. "Say \\"Hello\\"!" .. "\\\\ \\"" .. '</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// "test me","Say \"Hello\"!","\\ \""</span></pre>

<h3>
	إيجاد وسم بأكمله
</h3>

<p>
	اكتب تعبيرًا نمطيًا لإيجاد الوسم <code>&lt;style&gt;</code> كاملًا. قد لا يضم الوسم أية صفات attributes أو عددًا منها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_98" style="">
<span class="pun">&lt;</span><span class="pln">style type</span><span class="pun">=</span><span class="str">"..."</span><span class="pln"> id</span><span class="pun">=</span><span class="str">"..."</span><span class="pun">&gt;</span></pre>

<p>
	لا يجب أن يطابق التعبير الوسم <code>&lt;styler&gt;</code> مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_100" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/your regexp/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> </span><span class="str">'&lt;style&gt; &lt;styler&gt; &lt;style test="..."&gt;'</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// &lt;style&gt;, &lt;style test="..."&gt;</span></pre>

<p>
	<strong>الحل</strong>: من المؤكد أن بداية النمط هي <code>&lt;style</code>. لكن لا يمكن أن تكون بقية النمط ببساطة <code>&lt;?*.style&gt;</code> لأن وسمًا مثل <code>&lt;styler&gt;</code> سيكون نتيجة محتملة. نحتاج إلى فراغ بعد <code>&lt;style</code> ثم محارف اختيارية بعده أو النهاية، وبالتالي سيكون النمط المطلوب:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_102" style="">
<span class="pun">&lt;</span><span class="pln">style</span><span class="pun">(&gt;|</span><span class="pln">\s</span><span class="pun">.*?&gt;)</span></pre>

<p>
	إليك الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_105" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/&lt;style(&gt;|\s.*?&gt;)/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> </span><span class="str">'&lt;style&gt; &lt;styler&gt; &lt;style test="..."&gt;'</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// &lt;style&gt;, &lt;style test</span></pre>

<h3>
	إيجاد أعداد صحيحة موجبة
</h3>

<p>
	لدينا سلسلة نصية من الأعداد الصحيحة . أنشئ تعبيرًا نمطيًا يبحث عن الأعداد الموجبة فقط (يُسمح بالتقاط الصفر) مثال عن الاستخدام:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_107" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/your regexp/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"0 12 -5 123 -18"</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 0, 12, 123</span></pre>

<p>
	<strong>الحل</strong>: التعبير النمطي المناسب لإيجاد عدد صحيح هو :
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_109" style="">
<span class="pln"> \d</span><span class="pun">+</span></pre>

<p>
	يمكن التخلص من الأعداد السالبة باستخدام النظر خلفًا إلى الإشارة السالبة :
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_111" style="">
<span class="pln"> </span><span class="pun">(?&lt;!-)</span><span class="pln">\d</span><span class="pun">+</span></pre>

<p>
	لو استخدمنا هذا النمط فقد نلاحظ ظهور نتيجة إضافية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_113" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/(?&lt;!-)\d+/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"0 12 -5 123 -18"</span><span class="pun">;</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 0, 12, 123, 8</span></pre>

<p>
	كما ترى فقد وجد 8 بدلًا من -18. لحل المشكلة، لا بدّ من التحقق أن التعبير النمطي لن يبدأ المطابقة من منتصف رقم آخر غير مطابق. يمكن إنجاز الأمر بتخصيص عملية بحث أخرى للخلف عن الإشارة السالبة :
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_115" style="">
<span class="pun">(?&lt;!-)(?&lt;!</span><span class="pln">\d</span><span class="pun">)</span><span class="pln">\d</span><span class="pun">+</span></pre>

<p>
	يتحقق النمط
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_118" style="">
<span class="pln"> </span><span class="pun">(?&lt;!</span><span class="pln">\d</span><span class="pun">)</span></pre>

<p>
	أن المطابقة لن تبدأ بعد رقم آخر، بل فقط ما نحتاجه، ويمكن ضمهما أيضًا في عملية بحث للخلف واحدة: .
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_120" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/(?&lt;![-\d])\d+/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">"0 12 -5 123 -18"</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 0, 12, 123</span></pre>

<h3>
	إضافة شيفرة بعد الوسم Head
</h3>

<p>
	لدينا نص ضمن مستند <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a>. اكتب تعبيرًا نمطيًا يدخل الشيفرة <code>&lt;h1&gt;Hello&lt;/h1&gt;</code> بعد الوسم <code>&lt;body&gt;</code> مباشرة ولا تنس أنّ هذا الوسم قد يحوي صفات داخله.
</p>

<p>
	إليك مثالًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_122" style="">
<span class="pln">let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="str">/your regular expression/</span><span class="pun">;</span><span class="pln">

let str </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">html</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">body style</span><span class="pun">=</span><span class="str">"height: 200px"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">body</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">html</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">`;</span><span class="pln">
str </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">,</span><span class="pln"> </span><span class="pun">`&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="typ">Hello</span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;`);</span></pre>

<p>
	يجب أن تكون قيمة <code>str</code> بعد ذلك :
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_125" style="">
<span class="pun">&lt;</span><span class="pln">html</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">body style</span><span class="pun">=</span><span class="str">"height: 200px"</span><span class="pun">&gt;&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="typ">Hello</span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">body</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">html</span><span class="pun">&gt;</span></pre>

<p>
	<strong>الحل</strong>: علينا أولًا إيجاد الوسم <code>&lt;body&gt;</code>وذلك باستخدام التعبير النمطي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_127" style="">
<span class="pun">&lt;</span><span class="pln">body</span><span class="pun">.*?&gt;</span></pre>

<p>
	لا حاجة لتعديل الوسم <code>&lt;body&gt;</code> بل فقط إضافة النص بعده. إليك ما يمكن فعله:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_129" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">'...&lt;body style="..."&gt;...'</span><span class="pun">;</span><span class="pln">
str </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">/&lt;body.*?&gt;/</span><span class="pun">,</span><span class="pln"> </span><span class="str">'$&amp;&lt;h1&gt;Hello&lt;/h1&gt;'</span><span class="pun">);</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">str</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ...&lt;body style="..."&gt;&lt;h1&gt;Hello&lt;/h1&gt;...</span></pre>

<p>
	يعني النمط في النص المُستبدل التطابق الذاتي أي الجزء من النص الأصل المرتبط بالتعبير النمطي &lt;?*. body&gt; حيث سيُستبدل بنفسه إضافة إلى "<code>&lt;h1&gt;Hello&lt;/h1&gt;</code>".
</p>

<p>
	يقتضي الحل البديل استخدام البحث خلفًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_131" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> </span><span class="str">'...&lt;body style="..."&gt;...'</span><span class="pun">;</span><span class="pln">
str </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">/(?&lt;=&lt;body.*?&gt;)/</span><span class="pun">,</span><span class="pln"> </span><span class="pun">`&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="typ">Hello</span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;`);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">str</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ...&lt;body style="..."&gt;&lt;h1&gt;Hello&lt;/h1&gt;...</span></pre>

<p>
	لاحظ وجود جزء البحث خلفًا فقط في هذا التعبير النمطي حيث يعمل كالتالي:
</p>

<ul>
<li>
		تحقق عند كل موقع في النص إن تبعه النمط <code>&lt;?*. body&gt;</code>.
	</li>
	<li>
		إن كان الأمر كذلك فقد حصلنا على التطابق. لن يُعاد الوسم <code>&lt;?*. body&gt;</code> وستكون النتيجة نصًا فارغًا يتطابق الموقع الذي يتبعه النمط <code>&lt;?*. body&gt;</code>. عندها سيُستبدل النص الفارغ بالنمط <code>&lt;?*. body&gt;</code> يليه النص <code>&lt;h1&gt;Hello&lt;/h1&gt;</code>. يمكن الاستفادة من الرايات s و i في النمط كالتالي:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3601_133" style="">
<span class="str">/&lt;body.*?&gt;/</span><span class="pln">si</span></pre>

<p>
	تدفع الراية s المحرف "." إلى مطابقة سطر جديد وتدفع الراية i النص <code>&lt;body&gt;</code> لمطابقة النص <code>&lt;BODY&gt;</code> (تحسس لحالة الأحرف).
</p>

<p>
	ترجمة -وبتصرف- للفصول <a href="https://javascript.info/regexp-backreferences" rel="external nofollow">Backreferences in Patten</a> و<a href="https://javascript.info/regexp-alternation" rel="external nofollow">Alternation OR</a> و<a href="https://javascript.info/regexp-lookahead-lookbehind" rel="external nofollow">lookahead and lookbehind</a> و<a href="https://javascript.info/regexp-sticky" rel="external nofollow">sticky flag y, searching at position</a> من سلسلة <a href="https://javascript.info/" rel="external nofollow">The Modern JavaScript Tutorial</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regular-expressions-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1278/" rel="">التعابير النمطية Regular Expressions في جافاسكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%d9%85%d9%82%d8%af%d9%85%d8%a9-%d9%81%d9%8a-%d8%a7%d9%84%d8%aa%d8%b9%d8%a7%d8%a8%d9%8a%d8%b1-%d8%a7%d9%84%d9%86%d9%85%d8%b7%d9%8a%d8%a9-regular-expressions-r63/" rel="">مقدمة في التعابير النمطية Regular Expressions</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regexppcre-%D9%81%D9%8A-php-r1085/" rel="">التعابير النمطية (regexp/PCRE) في PHP</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1421</guid><pubDate>Fri, 14 Jan 2022 16:07:00 +0000</pubDate></item><item><title>&#x628;&#x64A;&#x626;&#x629; Node.js: &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; &#x62E;&#x627;&#x631;&#x62C; &#x627;&#x644;&#x645;&#x62A;&#x635;&#x641;&#x62D;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A8%D9%8A%D8%A6%D8%A9-nodejs-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D8%AE%D8%A7%D8%B1%D8%AC-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-r1402/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_12/61bc541d5895d_Node.js.png.122ba9a89096e1eb5ed3739d48c9c344.png" /></p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		سأل أحد الطلاب: "استخدم المبرمجون قديمًا حواسيب بدائية ولم يستخدموا لغات برمجة، لكنهم أنتجوا برامج رائعة رغم هذا فلماذا كل هذا التعقيد الذي نراه اليوم في البرمجة ولغات البرمجة؟".
	</p>

	<p>
		رد فو-تزو: استخدم البنّاءون الحطب والطين قديمًا، لكنهم بنوا رغم ذلك أكواخًا رائعةً.
	</p>

	<p>
		_ يوان-ما، كتاب البرمجة.
	</p>
</blockquote>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="85696" href="https://academy.hsoub.com/uploads/monthly_2021_12/chapter_picture_20.jpg.702faa2991c112336f73f48bf13cf25d.jpg" rel=""><img alt="chapter_picture_20.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="85696" data-unique="alymojdc5" src="https://academy.hsoub.com/uploads/monthly_2021_12/chapter_picture_20.jpg.702faa2991c112336f73f48bf13cf25d.jpg"></a>
</p>

<p>
	استخدمنا <a href="https://wiki.hsoub.com/JavaScript" rel="external">لغة جافاسكربت</a> طيلة المقالات الماضية من هذه السلسلة في بيئة واحدة هي بيئة المتصفح؛ أما في هذا المقال والذي يليه فسنلقي نظرةً سريعةً على <a href="https://academy.hsoub.com/programming/javascript/nodejs/express/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-nodejs-%D9%88express-r1099/" rel="">Node.js</a> البرنامج الذي يسمح لك بتطبيق مهاراتك في جافاسكربت خارج نطاق المتصفح، إذ تستطيع بناء أيّ شيء بها من أدوات أوامر الطرفية البسيطة إلى <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">خوادم HTTP</a> التي تشغِّل مواقعًا ديناميكيةً، كما يهدف هذان المقالان إلى شرح المفاهيم الأساسية التي تستخدمها Node.js وإعطاء معلومات تكفي لكتابة برامج لها، إلا أنهما لن يتعمقا في تفاصيل تلك المنصة.
</p>

<p>
	لن تعمل أمثلة الشيفرات التي ستكون في هذا المقال في المتصفح على عكس المقالات السابقة، فهي ليست جافاسكربت خام وليست مكتوبةً للمتصفح وإنما مكتوبة من أجل Node، فإذا أردت تشغيل هذه الشيفرات فستحتاج إلى تثبيت Node.js الإصدار 10.1 أو الأحدث بالذهاب إلى الموقع <a href="https://nodejs.org/" rel="external nofollow">https://nodejs.org</a> واتباع إرشادات التثبيت لنظام تشغيلك، كما ستجد هناك توثيقًا أكثر تفصيلًا عن Node.js.
</p>

<h2>
	تقديم إلى Node
</h2>

<p>
	تُعَدّ إدارة الدخل والخرج إحدى المشكلات الصعبة في كتابة أنظمة تتواصل عبر الشبكة، أي قراءة وكتابة البيانات من وإلى الشبكة والأقراص الصلبة، ذلك أنّ نقل البيانات من مكان لآخر يستغرق وقتًا؛ أما إذا جدولنا ذلك النقل فيمكن إحداث فرق كبير في سرعة استجابة النظام إلى طلبات المستخدِم أو الشبكة، كما تكون <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1304/" rel="">البرمجة غير المتزامنة asynchronous</a> مفيدةً في مثل تلك الحالات، فهي تسمح للبرنامج بإرسال واستقبال بيانات من وإلى أجهزة متعددة في الوقت نفسه دون إدارة معقدة للخيوط والتزامن.
</p>

<p>
	وُضع تصور Node في البداية من أجل تسهيل البرمجة غير المتزامنة، كما تتكامل <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%A7%D9%87%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA%D8%9F-%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D8%A7%D8%AA-r525/" rel="">جافاسكربت</a> جيدًا مع Node، فهي إحدى لغات البرمجة القليلة التي ليس لديها طريقة مضمَّنة لتنفيذ الدخل والخرج، وبالتالي يمكن استخدام جافاسكربت مع منظور Node غير المركزي للدخل والخرج دون أن يكون لدينا واجهتين غير متناسقتين، وقد نفَّذ الناس البرمجة المبنية على الاستدعاءات الخلفية في المتصفح في عام 2009 بالفعل حين صُمِّمت Node، وعليه فقد كان المجتمع الذي عاصر هذه اللغة معتادًا على تنسيق البرمجة غير المتزامن.
</p>

<h2>
	أمر node
</h2>

<p>
	توفِّر <a href="https://wiki.hsoub.com/Node.js" rel="external">Node.js</a> عند تثبيتها على النظام برنامجًا اسمه <code>node</code> يُستخدَم لتشغيل ملفات جافاسكربت، فلنقل مثلًا أنه لدينا ملفًا اسمه <code>hello.js</code> يحتوي الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_12" style="">
<span class="pln">let message </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Hello world"</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">message</span><span class="pun">);</span></pre>

<p>
	نستطيع تشغيل <code>node</code> من <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر</a> كما يلي لتنفيذ البرنامج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_17" style="">
<span class="pln">$ node hello</span><span class="pun">.</span><span class="pln">js
</span><span class="typ">Hello</span><span class="pln"> world</span></pre>

<p>
	ينفِّذ التابع <code>console.log</code> شيئًا شبيهًا بما يفعله في المتصفح أي سيطبع جزءًا من النص، لكن سيذهب النص في Node إلى مجرى الخرج القياسي للعملية بدلًا من منصة جافاسكربت التي في المتصفح، وهذا يعني أننا سنرى القيم المسجَّلة في طرفيتنا؛ أما إذا شغّلت <code>node</code> دون إعطائها ملفًا، فستزودك بمحث prompt تستطيع كتابة شيفرة جافاسكربت فيه وترى نتيجتها مباشرةً.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_19" style="">
<span class="pln">$ node
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
</span><span class="lit">2</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">[-</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">3</span><span class="pun">].</span><span class="pln">map</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">abs</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">
$</span></pre>

<p>
	تكون الرابطة <code>process</code> متاحةً عمومًا في Node شأنها في ذلك شأن الرابطة <code>console</code>، حيث توفِّر طرقًا مختلفةً لفحص وتعديل البرنامج الحالي؛ أما التابع <code>exit</code> فينهي العملية ويمكن إعطائه رمز حالة خروج تخبر البرنامج الذي بدأ <code>node</code> -وهي صدفية سطر الأوامر في هذه الحالة- هل اكتمل البرنامج بنجاح - أي الرمز صفر- أم قابل خطأ -أي رمز آخر-.
</p>

<p>
	تستطيع قراءة <code>process.argv</code> لإيجاد الوسائط التي أُعطيت للسكربت الخاصة بك والتي هي مصفوفة من سلاسل نصية، لاحظ أنها تتضمن أمر <code>node</code> نفسه واسم السكربت الخاص بك، وبالتالي تبدأ الوسائط الحقيقية عند الفهرس 2، فإذا احتوى <code>showargv.js</code> على التعليمة <code>‎console.log(process.argv)‎</code>، فستستطيع تشغيلها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_21" style="">
<span class="pln">$ node showargv</span><span class="pun">.</span><span class="pln">js one </span><span class="pun">--</span><span class="pln">and two
</span><span class="pun">[</span><span class="str">"node"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"/tmp/showargv.js"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"one"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"--and"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"two"</span><span class="pun">]</span></pre>

<p>
	توجد جميع رابطات جافاسكربت العامة مثل <code>Array</code> و<code>Math</code> و<code><a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">JSON</a></code> في بيئة Node على عكس الوظائف المتعلِّقة بالمتصفح مثل <code>document</code> و<code>prompt</code>.
</p>

<h2>
	الوحدات Modules
</h2>

<p>
	تضيف Node بعض الرابطات الإضافية في النطاق العام global scope على الرابطات التي ذكرناها قبل قليل، فإذا أردت الوصول إلى الوظائف المضمَّنة، فيمكنك طلب ذلك من <a href="https://wiki.hsoub.com/Node.js/modules" rel="external">نظام الوحدات module system</a>، وقد ذكرنا نظام وحدات CommonJS المبني على دالة <code>require</code> في مقال الوحدات Modules في جافاسكريبت المشار إليه أعلاه؛ أما هذا النظام فقد دُمِج في Node ويُستخدَم لتحميل أيّ شيء بدءًا من الوحدات المضمَّنة إلى الحزم المحمَّلة إلى الملفات التي هي جزء من برنامجك.
</p>

<p>
	يجب أن تحل Node السلسلة النصية المعطاة إلى ملف حقيقي يمكن تحميله عند استدعاء <code>require</code>، كما تُحَل أسماء المسارات التي تبدأ بـ <code>/</code> و<code>‎./‎</code> و<code>‎../‎</code> نسبيًا إلى مسار الوحدة الحالية؛ أما <code>.</code> فتشير إلى المجلد الحالي وتشير <code>‎../‎</code> إلى مجلد واحد للأعلى وتشير <code>/</code> إلى جذر نظام الملفات، فإذا طلبنا ‎<code>"./graph"</code>‎ من الملف <code>‎/tmp/robot/robot.js‎</code>، فستحاول Node تحميل الملف <code>‎/tmp/robot/graph.js</code>.
</p>

<p>
	يمكن إهمال الامتداد <code>‎.js‎</code>، كما ستضيفه Node تلقائيًا إذا وُجد ملف بذلك الاسم، فإذا أشار المسار المطلوب إلى مجلد، فستحاول Node تحميل الملف الذي يكون اسمه <code>index.js</code> في ذلك المجلد، وإذا أعطينا سلسلةً نصيةً لا تبدو مسارًا نسبيًا أو مطلقًا إلى الدالة <code>require</code>، فستفترض أنها تشير إما إلى وحدة مضمَّنة أو وحدة مثبَّتة في المجلد <code>node_modules</code>، كما تعطينا <code>‎require("fs")‎</code> مثلًا وحدة نظام الملفات المضمَّن في Node؛ أما <code>require("robot")‎‎</code> فستحاول تحميل المكتبة الموجودة في <code>‎node_modules/robot/‎</code>، ويمكن تثبيت مثل تلك المكتبات باستخدام NPM الذي سنعود إليه بعد قليل.
</p>

<p>
	لنعدّ الآن مشروعًا صغيرًا يتكون من ملفين، الأول اسمه <code>main.js </code>بحيث يعرّف سكربت يمكن استدعاؤها من سطر الأوامر لعكس سلسلة نصية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_26" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">reverse</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./reverse"</span><span class="pun">);</span><span class="pln">

</span><span class="com">// Index 2 holds the first actual command line argument</span><span class="pln">
let argument </span><span class="pun">=</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">argv</span><span class="pun">[</span><span class="lit">2</span><span class="pun">];</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">reverse</span><span class="pun">(</span><span class="pln">argument</span><span class="pun">));</span></pre>

<p>
	يعرِّف الملف <code>reverse.js</code> مكتبةً لعكس <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-strings-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r817/" rel="">السلاسل النصية</a> التي يمكن استخدامها بواسطة أداة سطر الأوامر هذه وكذلك بواسطة السكربتات الأخرى التي تحتاج إلى وصول مباشر إلى دالة عكس سلاسل نصية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_28" style="">
<span class="pln">exports</span><span class="pun">.</span><span class="pln">reverse </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Array</span><span class="pun">.</span><span class="pln">from</span><span class="pun">(</span><span class="pln">string</span><span class="pun">).</span><span class="pln">reverse</span><span class="pun">().</span><span class="pln">join</span><span class="pun">(</span><span class="str">""</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	تذكَّر أنّ إضافة الخصائص إلى <code>exports</code> يضيفها إلى واجهة الوحدة، وبما أنّ Node.js تعامل الملفات على أساس وحدات CommonJS، فيمكن أن تأخذ <code>main.js</code> دالة <code>reverse</code> المصدَّرة من <code>reverse.js</code>، ونستطيع الآن استدعاء أداتنا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_33" style="">
<span class="pln">$ node main</span><span class="pun">.</span><span class="pln">js </span><span class="typ">JavaScript</span><span class="pln">
tpircSavaJ</span></pre>

<h2>
	التثبيت باستخدام NPM
</h2>

<p>
	تعرَّضنا إلى مستودع NPM في مقال <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1286/" rel="">الوحدات Modules في جافاسكريبت</a> وهو مستودع لوحدات جافاسكربت، وقد كُتب الكثير منها من أجل Node، فإذا ثبَّت Node على حاسوبك، فستستطيع الحصول على أمر <code>npm</code> الذي يمكن استخدامه للتفاعل مع ذلك المستوع، والغرض الأساسي من <a href="https://academy.hsoub.com/programming/javascript/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-r1225/" rel="">NPM</a> هو تحميل الحِزم، حيث نستطيع استخدامه لجلب وتثبيت حزمة <code>ini</code> التي رأيناها في من قبل على حاسوبنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_36" style="">
<span class="pln">$ npm install ini
npm WARN enoent ENOENT</span><span class="pun">:</span><span class="pln"> no such file or directory</span><span class="pun">,</span><span class="pln">
         open </span><span class="str">'/tmp/package.json'</span><span class="pln">
</span><span class="pun">+</span><span class="pln"> ini@1</span><span class="pun">.</span><span class="lit">3.5</span><span class="pln">
added </span><span class="lit">1</span><span class="pln"> package in </span><span class="lit">0.552s</span><span class="pln">

$ node
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">parse</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"ini"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln"> parse</span><span class="pun">(</span><span class="str">"x = 1\ny = 2"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">{</span><span class="pln"> x</span><span class="pun">:</span><span class="pln"> </span><span class="str">'1'</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="str">'2'</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	يجب أن ينشئ NPM مجلدًا اسمه <code>node_modules</code> بعد تشغيل <code>npm install</code>، حيث سيكون مجلد <code>ini</code> بداخل ذلك المجلد محتويًا على المكتبة التي يمكن فتحها والاطلاع على شيفرتها، وتُحمَّل تلك المكتبة عند استدعاء <code>‎require("ini")‎</code>، كما نستطيع استدعاء الخاصية <code>parse</code> الخاصة بها لتحليل ملف الإعدادات.
</p>

<p>
	يثبِّت NPM الحِزم تحت المجلد الحالي افتراضيًا بدلًا من المكان المركزي، وقد يكون هذا غريبًا إذا كنا معتادين على مدير حِزم آخر، لكن هذا له مزاياه، فهو يجعل كل تطبيق متحكمًا بالكامل في الحِزم التي يثبِّتها ويسهِّل إدارة الإصدارات ومحو التطبيقات إذا أردنا حذفها.
</p>

<h2>
	ملفات الحزم
</h2>

<p>
	تستطيع رؤية تحذير في مثال <code>npm install</code> أنّ الملف <code>package.json</code> غير موجود، كما يُنصح بإنشاء مثل هذا الملف لكل مشروع إما يدويًا أو عبر تشغيل <code>npm init</code>، حيث يحتوي على بعض معلومات المشروع مثل اسمه وإصداره ويسرد اعتمادياته، ولنضرب مثلًا هنا بمحاكاة الروبوت من مقال <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D9%8A-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%B1%D8%AC%D9%84-%D8%A2%D9%84%D9%8A-%D8%B1%D9%88%D8%A8%D9%88%D8%AA-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1244/" rel="">مشروع تطبيقي لبناء رجل آلي (روبوت) عبر جافاسكريبت</a> التي عدلنا عليها عند تعرضنا للوحدات في مقال الوحدات Modules في جافاسكريبت، إذ قد يبدو ملف <code>package.json</code> الخاص بها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_39" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"author"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Marijn Haverbeke"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"eloquent-javascript-robot"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"description"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Simulation of a package-delivery robot"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"1.0.0"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"main"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"run.js"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"dependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"dijkstrajs"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^1.0.1"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"random-item"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^1.0.0"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"license"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"ISC"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عند تشغيل <code>npm install</code> دون تسمية الحزمة المراد تثبيتها، فسيثبِّت NPM الاعتماديات التي يسردها <code>package.json</code>؛ أما إذا ثبَّتت حزمةً ما ليست موجودة على أساس اعتمادية، فسيضيفها NPM إلى <code>package.json</code>.
</p>

<h2>
	الإصدارات
</h2>

<p>
	يسرد ملف <code>package.json</code> كلًا من إصدار البرنامج وإصدارات اعتمادياته، والإصدارات هي أسلوب للتعامل مع التطور المنفصل للحِزم، فقد لا تعمل الشيفرة التي كُتبت لحزمة في وقت ما مع الإصدار الجديد والمعدل من تلك الحزمة، حيث يشترط NPM أن تتبع الحِزم الخاصة به نظامًا اسمه الإصدار الدلالي semantic versioning الذي يرمّز بعض المعلومات عن الإصدارات المتوافقة التي لا تعطل الواجهة القديمة في رقم الإصدار version number، حيث يتكون الإصدار الدلالي من ثلاثة أعداد مفصولة بنقاط مثل <code>2.3.0</code>، وكل مرة تضاف فيها ميزة جديدة فإننا نزيد العدد الأوسط، وكل مرة تُعطل فيها التوافقية بحيث لا تعمل الشيفرة الحالية التي تستخدِم الحزمة مع إصدارها الجديد فإننا نغير العدد الأول من اليسار.
</p>

<p>
	يوضِّح محرف الإقحام <code>^</code> الذي يكون أمام رقم إصدار الاعتمادية في <code>package.json</code> أنّ أيّ نسخة متوافقة مع الرقم المعطى يمكن تثبيتها، وعلى ذلك تعني <code>‎"^2.3.0"‎</code> أنّ أيّ نسخة أكبر من أو يساوي 2.3.0 وأقل من 3.0.0 مسموح بها، كما يُستخدَم أمر <code>npm</code> أيضًا لنشر حزم جديدة أو إصدارات جديدة من الحزم، فإذا شغّلنا <code>npm publish</code> في مجلد فيه ملف <code>package.json</code>، فسينشر حزمةً بالاسم والإصدار الموجودَين في ملف <a href="https://academy.hsoub.com/programming/javascript/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-json-%D9%81%D9%8A-javascript-r548/" rel="">JSON</a> إلى السجل registry، ويستطيع أيّ أحد نشر حزم في NPM لكن شرط أن يكون اسم الحزمة غير مستخدَم من قبل.
</p>

<p>
	ليس ثمة شيء فريد في وظيفته بما أنّ برنامج <code>npm</code> جزء برمجي يتواصل مع نظام مفتوح هو سجل الحزم، ويمكن تثبيت برنامج آخر مثل <code>yarn</code> من سجل NPM ليؤدي وظيفة <code>npm</code> نفسها باستخدام واجهة مختلفة قليلًا وكذلك استراتيجية تثبيت مختلفة، لكن هذه السلسلة لا تتعمق في استخدام NPM، وإنما ننصحك بالرجوع إلى <a href="https://npmjs.org" rel="external nofollow">npmjs.org</a> لمزيد من التوثيق والبحث عن الحزم.
</p>

<h2>
	وحدة نظام الملفات
</h2>

<p>
	إحدى الوحدات المضمَّنة والمستخدَمة بكثرة في Node هي وحدة <code>fs</code> التي تشير إلى نظام الملفات file system، إذ تصدِّر الدوال من أجل العمل مع الملفات والمجلدات، حيث تقرأ مثلًا الدالة <code>readFile</code> ملفًا وتستدعي رد نداء بمحتويات الملف كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_44" style="">
<span class="pln">let </span><span class="pun">{</span><span class="pln">readFile</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"fs"</span><span class="pun">);</span><span class="pln">
readFile</span><span class="pun">(</span><span class="str">"file.txt"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"utf8"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">throw</span><span class="pln"> error</span><span class="pun">;</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"The file contains:"</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يشير الوسيط الثاني لدالة <code>readFile</code> إلى ترميز المحارف المستخدَم لفك تشفير الملف إلى سلسلة نصية، ورغم وجود عدة طرق لتشفير النصوص إلى بيانات ثنائية إلا أنّ أغلب النظم الحديثة تستخدِم UTF-8، فإذا لم يكن لديك سبب يجعلك تفضل ترميزًا آخر غير هذا فإنك تستخدمه، وعليه تمرر <code>"utf8"</code> عند قراءة ملف نصي، فإذا لم تمرر ترميزًا، فستفترض Node أنك تريد البيانات الثنائية وستعطيك الكائن <code>Buffer</code> بدلًا من سلسلة نصية، وهو كائن شبيه بالمصفوفة يحتوي أعدادًا تمثل البايتات (قطع بيانات بحجم 8 بت) التي في الملف.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_46" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">readFile</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"fs"</span><span class="pun">);</span><span class="pln">
readFile</span><span class="pun">(</span><span class="str">"file.txt"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> buffer</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">throw</span><span class="pln"> error</span><span class="pun">;</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"The file contained"</span><span class="pun">,</span><span class="pln"> buffer</span><span class="pun">.</span><span class="pln">length</span><span class="pun">,</span><span class="pln"> </span><span class="str">"bytes."</span><span class="pun">,</span><span class="pln">
              </span><span class="str">"The first byte is:"</span><span class="pun">,</span><span class="pln"> buffer</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تُستخدَم الدالة <code>writeFile</code> لكتابة ملف إلى القرص الصلب كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_48" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">writeFile</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"fs"</span><span class="pun">);</span><span class="pln">
writeFile</span><span class="pun">(</span><span class="str">"graffiti.txt"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Node was here"</span><span class="pun">,</span><span class="pln"> err </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Failed</span><span class="pln"> to write file</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">err</span><span class="pun">}`);</span><span class="pln">
  </span><span class="kwd">else</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"File written."</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	ليس من الضروري هنا تحديد الترميز، إذ ستفترض الدالة عند إعطائها سلسلة نصية أنّ عليها كتابتها على أساس نص باستخدام ترميزها الافتراضي للمحارف -أي UTF-8- ما لم يكن كائن <code>Buffer</code>.
</p>

<p>
	تحتوي الوحدة <code>fs</code> على عدة دوال أخرى مفيدة مثل <code>readdir</code> التي ستعيد الملفات الموجودة في مجلد على أساس مصفوفة من السلاسل النصية، و<code>stat</code> التي ستجلب معلومات عن ملف ما و<code>rename</code> التي ستعيد تسمية الملف و<code>unlink</code> التي ستحذِف الملفات وهكذا، ولمزيد من التفاصيل انظر <a href="https://wiki.hsoub.com/Node.js" rel="external">توثيق Node</a>، كما تأخذ أغلب الدوال السابقة دالة رد نداء على أساس آخر معامِل لها وتستدعيها إما مع خطأ -أي أول وسيط- أو مع نتيجة ناجحة -أي ثاني وسيط-، ورأينا في مقال <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1304/" rel="">البرمجة غير المتزامنة في جافاسكريبت</a> وجود تبعات لمثل هذا التنسيق من البرمجة لعل أكبرها أن عملية معالجة الأخطاء نفسها تصبح طويلة وعرضة للخطأ.
</p>

<p>
	لا زال تكامل الوعود مع Node قيد التطوير وقت كتابة هذه الكلمات رغم أنها أُدخلت في جافاسكربت منذ مدة لا بأس بها، فلدينا الكائن <code>promises</code> المصدَّر من حزمة <code>fs</code> منذ الإصدار 10.1 والذي يحتوي على أغلب الدوال الموجودة في <code>fs</code> لكنه يستخدِم الوعود بدلًا من دوال رد النداء.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_50" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">readFile</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"fs"</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">
readFile</span><span class="pun">(</span><span class="str">"file.txt"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"utf8"</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">text </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"The file contains:"</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">));</span></pre>

<p>
	قد لا نحتاج أحيانًا إلى اللاتزامنية، بل قد تعيق عملنا، ولحسن حظنا أنّ أغلب الدوال التي في <code>fs</code> نسخة تزامنية لها الاسم نفسه مع لاحقة <code>Sync</code> مضافة إلى آخرها، فسيكون اسم النسخة التزامنية من دالة <code>readFile</code> مثلًا <code>readFileSync</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_52" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">readFileSync</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"fs"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"The file contains:"</span><span class="pun">,</span><span class="pln">
            readFileSync</span><span class="pun">(</span><span class="str">"file.txt"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"utf8"</span><span class="pun">));</span></pre>

<p>
	لاحظ أنّ البرنامج سيتوقف عن العمل تمامًا أنه ريثما تُنفذ العملية التزامنية، وسيكون ذلك تأخيرًا مزعجًا إذا كان يُفترض به الاستجابة إلى المستخدِم أو إلى آلات أخرى في الشبكة أثناء تلك العملية.
</p>

<h2>
	وحدة HTTP
</h2>

<p>
	لدينا وحدة مركزية أخرى توفِّر وظيفة تشغيل خوادم HTTP وإنشاء طلبات HTTP كذلك واسمها <code>http</code>، وإذا أردنا تشغيل خادم HTTP فسيكون ذلك عبر السكربت التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_54" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">createServer</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"http"</span><span class="pun">);</span><span class="pln">
let server </span><span class="pun">=</span><span class="pln"> createServer</span><span class="pun">((</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  response</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/html"</span><span class="pun">});</span><span class="pln">
  response</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(`</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="typ">Hello</span><span class="pun">!&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">p</span><span class="pun">&gt;</span><span class="typ">You</span><span class="pln"> asked </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">code</span><span class="pun">&gt;</span><span class="pln">$</span><span class="pun">{</span><span class="pln">request</span><span class="pun">.</span><span class="pln">url</span><span class="pun">}&lt;</span><span class="str">/code&gt;&lt;/</span><span class="pln">p</span><span class="pun">&gt;`);</span><span class="pln">
  response</span><span class="pun">.</span><span class="pln">end</span><span class="pun">();</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
server</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="lit">8000</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Listening! (port 8000)"</span><span class="pun">);</span></pre>

<p>
	فإذا شغّلت هذه السكربت على الحاسوب، فستوجِّه المتصفح إلى <a href="http://localhost:8000/hello" ipsnoembed="false" rel="external nofollow">http://localhost:8000/hello</a> لإنشاء طلب إلى خادمك، وسيستجيب بصفحة <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> صغيرة، كما تُستدعى الدالة الممررة على أساس وسيط إلى <code>createServer</code> في كل مرة يتصل عميل بالخادم، كذلك تُعَدّ الرابطتان <code>request</code> و<code>response</code> كائنين يمثلان البيانات الواردة والصادرة، حيث يحتوي الأول على معلومات عن الطلب مثل خاصية <code>url</code> الخاصة به والتي تخبرنا بالرابط URL الذي أُنشئ الطلب إليه، لذلك عندما نفتح تلك الصفحة في المتصفح فإنها ترسل طلبًا إلى حاسوبك، وهذا يشغّل دالة الخادم ويرجع استجابةً نراها في المتصفح.
</p>

<p>
	أما لإرجاع شيء من طرفنا فستستدعي عدة توابع على الكائن <code>response</code>، أولها هو التابع <code>writeHead</code> الذي يكتب ترويسات الاستجابة -انظر مقال <a href="https://academy.hsoub.com/programming/javascript/http-%D9%88%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1367/" rel="">HTTP والاستمارات في جافاسكربت</a>-، وتعطيه شيفرة الحالة -أي 200 التي تعني OK في هذه الحالة-، وكائنًا يحتوي على قيم الترويسة، ثم يعيِّن المثال ترويسة <code>Content-Type</code> لتخبر العميل أننا نرسل مستند HTML إليه، ثم يُرسَل متن الاستجابة الفعلية -أي المستند نفسه- باستخدام <code>response.write</code>، ويُسمح لنا باستدعاء هذا التابع عدة مرات إذا أردنا إرسال الاستجابة جزءًا جزءًا، كما في حالة بث البيانات إلى العميل كلما صارت متاحةً على سبيل المثال، ثم تشير <code>response.end</code> إلى نهاية الاستجابة.
</p>

<p>
	يتسبب استدعاء <code>server.listen</code> في جعل الخادم ينتظر اتصالًا على المنفَذ 8000 وهذا هو السبب الذي يجبرنا على الاتصال بـ localhost:8000 من أجل التواصل مع هذا الخادم بدلًا من localhost فقط والتي ستستخدِم المنفَذ 80، وتظل العملية في وضع انتظار عند تشغيل هذه السكربت، ولن تخرج <code>node</code> تلقائيًا عندما تصل إلى نهاية السكربت بما أنها تظل في وضع استماع إلى الإحداث وانتظارها والتي هي اتصالات الشبكة في هذه الحالة، كما نضغط control+c من أجل إغلاقها، وكان هذا الخادم مثالًا فقط، وإلا فإنّ خادم الويب الحقيقي يفعل أكثر من ذلك، فهو ينظر في تابع الطلب -أي الخاصية <code>method</code>- ليرى الإجراء الذي يحاول العميل تنفيذه، وينظر في رابط الطلب كي يعرف المورد الذي ينفَّذ عليه ذلك الإجراء، وسنرى لاحقًا في هذا المقال خادمًا أكثر تقدمًا وتعقيدًا.
</p>

<p>
	يمكننا استخدام الدالة <code>request</code> في وحدة <code>http</code> من أجل التصرف على أساس عميل HTTP.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_56" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">request</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"http"</span><span class="pun">);</span><span class="pln">
let requestStream </span><span class="pun">=</span><span class="pln"> request</span><span class="pun">({</span><span class="pln">
  hostname</span><span class="pun">:</span><span class="pln"> </span><span class="str">"eloquentjavascript.net"</span><span class="pun">,</span><span class="pln">
  path</span><span class="pun">:</span><span class="pln"> </span><span class="str">"/20_node.html"</span><span class="pun">,</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">"GET"</span><span class="pun">,</span><span class="pln">
  headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="typ">Accept</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/html"</span><span class="pun">}</span><span class="pln">
</span><span class="pun">},</span><span class="pln"> response </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Server responded with status code"</span><span class="pun">,</span><span class="pln">
              response</span><span class="pun">.</span><span class="pln">statusCode</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
requestStream</span><span class="pun">.</span><span class="pln">end</span><span class="pun">();</span></pre>

<p>
	يهيئ الوسيط الأول للدالة <code>request</code> الطلب ليخبر Node بالخادم الذي يجب التواصل معه والمسار الذي تطلبه من ذلك الخادم وأيّ تابع يجب استخدامه وهكذا؛ أما الوسيط الثاني فيكون الدالة التي يجب أن تستدعَى عندما تأتي الاستجابة، وتعطى كائنًا يسمح لنا بفحص الاستجابة، لمعرفة رمز حالتها مثلًا، كما يسمح الكائن الذي تعيده <code>request</code> ببث البيانات في الطلب باستخدام التابع <code>write</code> كما في كائن <code>response</code> الذي رأيناه في الخادم وتنهي الطلب بالتابع <code>end</code>، ولا يستخدم المثال <code>write</code> لأن طلبات <code>GET</code> يجب ألا تحتوي على بيانات في متونها.
</p>

<p>
	لدينا دالة <code>request</code> مشابهةً في وحدة <code>https</code>، حيث يمكن استخدامها لإنشاء طلبات إلى روابط <code>‎https:‎</code>، ولا شك أنّ إنشاء الطلبات باستخدام Node الخام أمر طويل مسهب، لهذا توجد حزم تغليف سهلة الاستخدام متاحة في NPM مثل <code>node-fetch</code> التي توفر واجهة <code>fetch</code> مبنيةً على الوعود التي عرفناها من المتصفح.
</p>

<h2>
	البث Stream
</h2>

<p>
	رأينا نسختين من البث القابل للكتابة writable stream في مثالَي HTTP، هما كائن الاستجابة الذي يستطيع الخادم كتابته، وكائن الطلب الذي أعادته <code>request</code>، حيث يُستخدَم البث القابل للكتابة كثيرًا في Node، فمثل تلك الكائنات لها تابع اسمه <code>write</code> يُمكن تمرير سلسلة نصية إليه، أو كائن <code>Buffer</code> لكتابة شيء في البث؛ أما التابع <code>end</code> فيغلق البث ويأخذ قيمةً -بصورة اختيارية- للكتابة في البث قبل الإغلاق، ويمكن إعطاء كلا التابعَين السابقًين رد نداء على أساس وسيط إضافي، حيث يستدعيانه عند انتهاء الكتابة أو الإغلاق.
</p>

<p>
	يمكن إنشاء بث قابل للكتابة يشير إلى ملف باستخدام دالة <code>createWriteStream</code> من وحدة <code>fs</code>، ثم يمكنك استخدام التابع <code>write</code> على الكائن الناتج من أجل كتابة الملف جزءًا واحدًا في كل مرة بدلًا من كتابته على مرة واحدة كما في <code>writeFile</code>؛ أما البث القابل للقراءة ففيه تفصيل أكثر، فرابطة <code>request</code> التي مُرِّرت إلى الاستدعاء الخلفي لخادم HTTP قابلة للقراءة، وكذلك رابطة <code>response</code> الممررة إلى رد نداء العميل HTTP. حيث يقرأ الخادم الطلبات ثم يكتب الاستجابات، بينما يكتب العميل الطلب أولًا ثم يقرأ الاستجابة، وتتم القراءة من البث باستخدام معالجات الأحداث بدلًا من التوابع.
</p>

<p>
	تملك الكائنات التي تطلق الأحداث في Node تابعًا اسمه <code>on</code> يشبه التابع <code>addEventListener</code> الموجود في المتصفح، كما يمكن إعطاؤه اسم حدث ثم دالة، وسيسجل تلك الدالة لتُستدعى كلما وقع ذلك الحدث، كذلك فإن البث القابل للقراءة له حدثان هما <code>"data"</code> و<code>"end"</code>، حيث يُطلَق الأول في كل مرة تأتي بيانات فيها؛ أما الثاني فيُستدعى كلما كان البث عند نهايته، وهذا النموذج مناسب لبث البيانات التي يمكن معالجتها فورًا حتى لو كان باقي المستند غير متاح بعد، كما يُقرأ الملف على أساس بث قابل للقراءة من خلال استخدام دالة <code>createReadStream</code> من وحدة <code>fs</code>، وتنشئ الشيفرة التالية خادمًا يقرأ متون الطلبات ويبثها مرةً أخرى إلى العميل على أساس نص حروفه من الحالة الكبيرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_58" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">createServer</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"http"</span><span class="pun">);</span><span class="pln">
createServer</span><span class="pun">((</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  response</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span class="pun">});</span><span class="pln">
  request</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">"data"</span><span class="pun">,</span><span class="pln"> chunk </span><span class="pun">=&gt;</span><span class="pln">
    response</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">().</span><span class="pln">toUpperCase</span><span class="pun">()));</span><span class="pln">
  request</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">"end"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">end</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}).</span><span class="pln">listen</span><span class="pun">(</span><span class="lit">8000</span><span class="pun">);</span></pre>

<p>
	ستكون القيمة <code>chunk</code> الممررة إلى معالج البيانات على هيئة <code>Buffer</code> ثنائي، ويمكن تحويل ذلك إلى سلسلة نصية بفك تشفيرها على أساس محارف UTF-8 مرمزة باستخدام التابع <code>toString</code>، منا ترسب الشيفرة التالية عند تشغيلها أثناء نشاط خادم الحروف الكبيرة طلبًا إلى ذلك الخادم وتكتب الاستجابة التي تحصل عليها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_60" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">request</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"http"</span><span class="pun">);</span><span class="pln">
request</span><span class="pun">({</span><span class="pln">
  hostname</span><span class="pun">:</span><span class="pln"> </span><span class="str">"localhost"</span><span class="pun">,</span><span class="pln">
  port</span><span class="pun">:</span><span class="pln"> </span><span class="lit">8000</span><span class="pun">,</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">"POST"</span><span class="pln">
</span><span class="pun">},</span><span class="pln"> response </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  response</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">"data"</span><span class="pun">,</span><span class="pln"> chunk </span><span class="pun">=&gt;</span><span class="pln">
    process</span><span class="pun">.</span><span class="pln">stdout</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">()));</span><span class="pln">
</span><span class="pun">}).</span><span class="pln">end</span><span class="pun">(</span><span class="str">"Hello server"</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → HELLO SERVER</span></pre>

<p>
	يكتب المثال إلى <code>process.stout</code> والذي يُعَدّ خرجًا قياسيًا للعملية وبثًا قابلًا للكتابة، بدلًا من استخدام <code>console.log</code>، إذ لا نستطيع استخدام <code>console.log</code> لأنها تضيف محرف سطر جديد إضافي بعد كل جزء تكتبه من النص، وهذا ليس مناسبًا هنا بما أنّ الاستجابة قد تأتي في هيئة عدة كتل نصية.
</p>

<h2>
	خادم الملفات
</h2>

<p>
	نريد الآن دمج المعلومات التي عرفناها عن خوادم HTTP والعمل مع نظام الملفات لإنشاء جسر بين خادم HTTP يسمح بالوصول البعيد إلى نظام ملفات، كما يكون في مثل تلك الخوادم جميع أنواع الاستخدامات الممكنة، فهي تسمح لتطبيقات الويب بتخزين البيانات ومشاركتها، أو تعطي مجموعةً من الناس وصولًا مشتركًا إلى مجموعة ملفات، ويمكن استخدام التوابع <code>GET</code> و<code>PUT</code> و<code>DELETE</code> لقراءة وكتابة وحذف الملفات على الترتيب، وذلك عندما نعامل الملفات على أساس موارد HTTP، كما سنفسر المسار الذي في الطلب على أنه مسار الملف الذي يشير إليه الطلب، ولعلنا لا نريد مشاركة كل نظام الملفات الخاص بنا، لذا سنفسر تلك المسارات على أنها تبدأ في المجلد العامل للخادم وهو المجلد الذي بدأ فيه.
</p>

<p>
	فإذا شغّلنا الخادم من <code>‎/tmp/public/‎</code> أو على ويندوز من <code>‎C:\tmp\public\‎</code>، فسيشير طلب <code>‎/file.txt‎</code> إلى <code>‎‎/tmp/public/file.txt</code> أو <code>‎C:\tmp\public\file.txt‎</code> على ويندوز، كما سنبني البرنامج جزءًا جزءًا مستخدِمين الكائن <code>methods</code> لتخزين الدوال التي تعالج توابع HTTP المختلفة، وتكون معالجات التوابع دوال <code>async</code> تحصل على كائن الطلب على أساس وسيط وتعيد وعدًا يُحل إلى كائن يصف الاستجابة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_62" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">createServer</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"http"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> methods </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">create</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">);</span><span class="pln">

createServer</span><span class="pun">((</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let handler </span><span class="pun">=</span><span class="pln"> methods</span><span class="pun">[</span><span class="pln">request</span><span class="pun">.</span><span class="pln">method</span><span class="pun">]</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> notAllowed</span><span class="pun">;</span><span class="pln">
  handler</span><span class="pun">(</span><span class="pln">request</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">error </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">.</span><span class="pln">status </span><span class="pun">!=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> error</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">body</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">(</span><span class="pln">error</span><span class="pun">),</span><span class="pln"> status</span><span class="pun">:</span><span class="pln"> </span><span class="lit">500</span><span class="pun">};</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(({</span><span class="pln">body</span><span class="pun">,</span><span class="pln"> status </span><span class="pun">=</span><span class="pln"> </span><span class="lit">200</span><span class="pun">,</span><span class="pln"> type </span><span class="pun">=</span><span class="pln"> </span><span class="str">"text/plain"</span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       response</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="pln">status</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> type</span><span class="pun">});</span><span class="pln">
       </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">body </span><span class="pun">&amp;&amp;</span><span class="pln"> body</span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">)</span><span class="pln"> body</span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">response</span><span class="pun">);</span><span class="pln">
       </span><span class="kwd">else</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="pln">body</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}).</span><span class="pln">listen</span><span class="pun">(</span><span class="lit">8000</span><span class="pun">);</span><span class="pln">

async </span><span class="kwd">function</span><span class="pln"> notAllowed</span><span class="pun">(</span><span class="pln">request</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    status</span><span class="pun">:</span><span class="pln"> </span><span class="lit">405</span><span class="pun">,</span><span class="pln">
    body</span><span class="pun">:</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Method</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">request</span><span class="pun">.</span><span class="pln">method</span><span class="pun">}</span><span class="pln"> not allowed</span><span class="pun">.`</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يبدأ هذا خادمًا لا يعيد إلا استجابات خطأ 405، وهو الرمز الذي يشير إلى رفض الخادم لمعالجة التابع المعطى.
</p>

<p>
	يحوِّل استدعاء <code>catch</code> عند رفض وعد معالِج الطلب الخطأ إلى كائن استجابة إذا لم يكن هو كائن استجابة بالفعل، وذلك كي يستطيع الخادم إرسال استجابة خطأ مرةً أخرى لإخبار العميل أنه فشل في معالجة الطلب، كما يمكن إهمال حقل <code>status</code> في وصف الاستجابة وتكون حينئذ 200 افتراضيًا -وهي التي تعني OK-؛ أما نوع المحتوى في الخاصية <code>type</code> فيمكن إهماله كذلك، ويفترض حينها أنّ الاستجابة نص مجرد.
</p>

<p>
	حين تكون قيمة <code>body</code> بثًا قابلًا للقراءة فسيحتوي على التابع <code>pipe</code> الذي يُستخدَم لإعادة توجيه كل المحتوى من بث قابل للقراءة إلى بث قابل للكتابة، وإلا فيُفترض أنه إما <code>null</code> -أي لا شيء- أو سلسلةً نصيةً أو مخزنًا مؤقتًا buffer، ويُمرَّر مباشرة إلى التابع <code>end</code> الخاص بالاستجابة، كما تستخدِم الدالة <code>urlPath</code> وحدة <code>url</code> المضمَّنة لتحليل الرابط من أجل معرفة مسار الملف المتوافق مع رابط الطلب، وهي تأخذ اسم المسار الذي يكون شيئًا مثل <code>‎"/file.txt"‎</code> وتفك تشفيره لتتخلص من رموز التهريب التي على شاكلة <code>‎%20‎</code> وتحله نسبة إلى المجلد العامل للبرنامج.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_64" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">parse</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"url"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> sep</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"path"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> baseDirectory </span><span class="pun">=</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">cwd</span><span class="pun">();</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> urlPath</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let </span><span class="pun">{</span><span class="pln">pathname</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> parse</span><span class="pun">(</span><span class="pln">url</span><span class="pun">);</span><span class="pln">
  let path </span><span class="pun">=</span><span class="pln"> resolve</span><span class="pun">(</span><span class="pln">decodeURIComponent</span><span class="pun">(</span><span class="pln">pathname</span><span class="pun">).</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">1</span><span class="pun">));</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">path </span><span class="pun">!=</span><span class="pln"> baseDirectory </span><span class="pun">&amp;&amp;</span><span class="pln">
      </span><span class="pun">!</span><span class="pln">path</span><span class="pun">.</span><span class="pln">startsWith</span><span class="pun">(</span><span class="pln">baseDirectory </span><span class="pun">+</span><span class="pln"> sep</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="pun">{</span><span class="pln">status</span><span class="pun">:</span><span class="pln"> </span><span class="lit">403</span><span class="pun">,</span><span class="pln"> body</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Forbidden"</span><span class="pun">};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> path</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب عليك القلق بشأن الأمان عند تهيئة وضبط البرنامج ليقبل طلبات الشبكة، ففي حالتنا من الممكن كشف كل نظام الملفات إلى الشبكة إذا لم نتوخَّ الحذر.
</p>

<p>
	تُعَدّ مسارات الملفات سلاسل نصية في Node، حيث يلزمنا قدر لا بأس به من التفسير interpretation لربط مثل تلك السلسلة النصية بملف حقيقي، فقد تحتوي المسارات على <code>‎../‎</code> لتشير إلى المجلد الأب، وعليه يكون أحد المصادر البدهية للمشكلة هي طلبات إلى مسارات مثل <code>‎‎/../secret_file</code>، ومن أجل تجنب مثل تلك المشاكل تستخدِم <code>urlPath</code> الدالة <code>resolve</code> من وحدة <code>path</code> التي تحل الروابط النسبية، ثم تتحقق من كون النتيجة تحت المجلد العامل دائمًا، كما يمكن استخدام الدالة <code>process.cwd</code> لإيجاد ذلك المجلد العامل، حيث تشير cwd إلى المجلد العامل الحالي أو current working directory.
</p>

<p>
	أما رابطة <code>sep</code> من حزمة <code>path</code> فهي فاصلة مسار النظام، وهي شرطة مائلة خلفية على ويندوز وأمامية على أغلب نظم التشغيل الأخرى، فإذا لم يبدأ المسار بالمجلد الرئيسي فسترفع الدالة كائن استجابة خطأ باستخدام رمز حالة HTTP يشير إلى استحالة الوصول إلى المورد، وسنضبط التابع <code>GET</code> كي يعيد قائمةً من الملفات عند قراءة مجلد ويعيد محتوى الملف عند قراءة ملف عادي؛ أما السؤال الذي يطرح نفسه هنا هو نوع ترويسة <code>Content-Type</code> التي يجب تعيينها عند إعادة محتوى الملف، فبما أن تلك الملفات قد تكون أيّ شيء فلا يستطيع الخادم إعادة نوع المحتوى نفسه في كل مرة، ونستخدِم هنا NPM مرةً أخرى، إذ تعرف حزمة <code>mime</code> النوع الصحيح لعدد كبير من امتدادات الملفات، كما تسمى موضحات أنواع المحتوى مثل <code>text/plain</code> باسم mime، يثبّت الأمر <code>npm</code> أدناه إصدارًا محددًا من <code>mime</code> في المجلد الذي فيه سكربت الخادم:
</p>

<pre class="ipsCode">
$ npm install mime@2.2.0
</pre>

<p>
	يعاد رمز الحالة 404 إذا لم يكن الملف المطلوب موجودًا، وسنستخدم الدالة <code>stat</code> التي تبحث عن معلومات تتعلق بملف ما لتعرف هل الملف موجود أم لا وهل هو مجلد أم لا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_66" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">createReadStream</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"fs"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">stat</span><span class="pun">,</span><span class="pln"> readdir</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"fs"</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> mime </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"mime"</span><span class="pun">);</span><span class="pln">

methods</span><span class="pun">.</span><span class="pln">GET </span><span class="pun">=</span><span class="pln"> async </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">request</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let path </span><span class="pun">=</span><span class="pln"> urlPath</span><span class="pun">(</span><span class="pln">request</span><span class="pun">.</span><span class="pln">url</span><span class="pun">);</span><span class="pln">
  let stats</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    stats </span><span class="pun">=</span><span class="pln"> await stat</span><span class="pun">(</span><span class="pln">path</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">.</span><span class="pln">code </span><span class="pun">!=</span><span class="pln"> </span><span class="str">"ENOENT"</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">throw</span><span class="pln"> error</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">status</span><span class="pun">:</span><span class="pln"> </span><span class="lit">404</span><span class="pun">,</span><span class="pln"> body</span><span class="pun">:</span><span class="pln"> </span><span class="str">"File not found"</span><span class="pun">};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">stats</span><span class="pun">.</span><span class="pln">isDirectory</span><span class="pun">())</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">body</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">await readdir</span><span class="pun">(</span><span class="pln">path</span><span class="pun">)).</span><span class="pln">join</span><span class="pun">(</span><span class="str">"\n"</span><span class="pun">)};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">body</span><span class="pun">:</span><span class="pln"> createReadStream</span><span class="pun">(</span><span class="pln">path</span><span class="pun">),</span><span class="pln">
            type</span><span class="pun">:</span><span class="pln"> mime</span><span class="pun">.</span><span class="pln">getType</span><span class="pun">(</span><span class="pln">path</span><span class="pun">)};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	تكون <code>stat</code> لا تزامنيةً لأنها ستحتاج أن تتعامل مع القرص وستأخذ وقتًا لذلك، وبما أننا نستخدِم الوعود بدلًا من تنسيق رد النداء فيجب استيراده من <code>promises</code> بدلًا من <code>fs</code> مباشرةً، حيث ترفع <code>stat</code> كائن خطأ به الخاصية <code>code</code> لـ <code>"ENOENT"</code> إذا لم يكن الملف موجودًا، وإذا بدت هذه الرموز غريبةً عليك لأول وهلة فاعلم أنها متأثرة بأسلوب نظام يونكس، كما ستجد أنواع الخطأ في Node على مثل هذه الشاكلة.
</p>

<p>
	يخبرنا الكائن <code>stats</code> الذي تعيده <code>stat</code> بمعلومات عديدة عن الملف مثل حجمه -أي الخاصية <code>size</code>- وتاريخ التعديل عليه -أي الخاصية <code>mtime</code>- وهل هذا الملف مجلد أم ملف عادي من خلال التابع <code>isDirectory</code>، كما نستخدِم <code>readdir</code> لقراءة مصفوفة ملفات في المجلد ونعيدها إلى العميل؛ أما بالنسبة للملفات العادية فسننشئ بثًا قابلًا للقراءة باستخدام <code>createReadStream</code> ونعيده على أنه المتن مع نوع المحتوى الذي تعطينا إياه الحزمة <code>mime</code> لاسم الملف، كما تكون الشيفرة التي تعالج طلبات <code>DELETE</code> أبسط قليلًا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_68" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">rmdir</span><span class="pun">,</span><span class="pln"> unlink</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"fs"</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">

methods</span><span class="pun">.</span><span class="pln">DELETE </span><span class="pun">=</span><span class="pln"> async </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">request</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let path </span><span class="pun">=</span><span class="pln"> urlPath</span><span class="pun">(</span><span class="pln">request</span><span class="pun">.</span><span class="pln">url</span><span class="pun">);</span><span class="pln">
  let stats</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    stats </span><span class="pun">=</span><span class="pln"> await stat</span><span class="pun">(</span><span class="pln">path</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">.</span><span class="pln">code </span><span class="pun">!=</span><span class="pln"> </span><span class="str">"ENOENT"</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">throw</span><span class="pln"> error</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">status</span><span class="pun">:</span><span class="pln"> </span><span class="lit">204</span><span class="pun">};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">stats</span><span class="pun">.</span><span class="pln">isDirectory</span><span class="pun">())</span><span class="pln"> await rmdir</span><span class="pun">(</span><span class="pln">path</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">else</span><span class="pln"> await unlink</span><span class="pun">(</span><span class="pln">path</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">status</span><span class="pun">:</span><span class="pln"> </span><span class="lit">204</span><span class="pun">};</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	إذا لم تحتوي استجابة HTTP على أيّ بيانات فيمكن استخدام رمز الحالة 204 ("لا محتوى") لتوضيح ذلك، وهو الخيار المنطقي هنا بما أنّ الاستجابة للحذف لا تحتاج أيّ معلومات أكثر من تأكيد نجاح العملية، لكن لماذا نحصل على رمز حالة يفيد النجاح عند محاولة حذف ملف غير موجود أصلًا؟ أليس من المنطقي أن نحصل على خطأ؟
</p>

<p>
	يرجع ذلك إلى معيار HTTP الذي يشجعنا على جعل الطلبات راسخة idempotent، مما يعني سيعطينا تكرار الطلب نفسه النتيجة نفسها التي خرجت في أول مرة، فإذا حاولنا حذف شيء ليس موجودًا فيمكن القول أنّ التأثير الذي كنا نحاول إحداثه قد وقع -وهو فعل الحذف-، فلم يَعد العنصر الذي نريد حذفه موجودًا، وكأن هدف الطلب قد تحقق كما لو كان موجودًا ثم حذفناه بطلبنا، كما تمثِّل الشيفرة التالية معالِج طلبات <code>PUT</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_70" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">createWriteStream</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"fs"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> pipeStream</span><span class="pun">(</span><span class="pln">from</span><span class="pun">,</span><span class="pln"> to</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    from</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">"error"</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">);</span><span class="pln">
    to</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">"error"</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">);</span><span class="pln">
    to</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">"finish"</span><span class="pun">,</span><span class="pln"> resolve</span><span class="pun">);</span><span class="pln">
    from</span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">to</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

methods</span><span class="pun">.</span><span class="pln">PUT </span><span class="pun">=</span><span class="pln"> async </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">request</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let path </span><span class="pun">=</span><span class="pln"> urlPath</span><span class="pun">(</span><span class="pln">request</span><span class="pun">.</span><span class="pln">url</span><span class="pun">);</span><span class="pln">
  await pipeStream</span><span class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> createWriteStream</span><span class="pun">(</span><span class="pln">path</span><span class="pun">));</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">status</span><span class="pun">:</span><span class="pln"> </span><span class="lit">204</span><span class="pun">};</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	لسنا في حاجة إلى التحقق من وجود الملف هذه المرة، فإذا كان موجودًا فسنكتب فوقه، ونستخدِم <code>pipe</code> هنا مرةً أخرى لنقل البيانات من البث القابل للقراءة إلى بث قابل للكتابة، وفي حالتنا هذه من الطلب إلى الملف، لكن بما أنّ <code>pipe</code> ليست مكتوبةً لتعيد وعدًا، فعلينا كتابة مغلِّف هو <code>pipeStream</code> الذي ينشئ وعدًا حول ناتج استدعاء <code>pipe</code>، كما سيعيد <code>createWriteStream</code> بثًا إذا حدث خطأ أثناء فتح الملف لكن سيطلق ذلك البث حدث <code>"error"</code>، وقد يفشل البث من الطلب كما في حالة انقطاع الشبكة، لذا فإننا نوصل الحدثين <code>"error"</code> لكلا البثين كي يرفضا الوعد.
</p>

<p>
	سيغلق بث الخرج الذي يتسبب في إطلاق الحدث <code>"finish"</code> عند انتهاء <code>pipe</code>، وهي النقطة التي يمكننا حل الوعد فيها بنجاح -أي لا نعيد شيئًا-، كما يمكن العثور على السكربت الكاملة للخادم في <a href="https://eloquentjavascript.net/code/file_server.js" ipsnoembed="true" rel="external nofollow">https://eloquentjavascript.net/code/file_server.js</a> وهي متاحة للتحميل، وتستطيع بدء خادم الملفات الخاص بك بتحميلها وتثبيت اعتمادياتها ثم تشغيلها مع Node، كما تستطيع تعديلها وتوسيعها لحل تدريبات هذا المقالأو للتجربة، وتُستخدم أداة سطر الأوامر  <code>curl</code> لإنشاء طلبات HTTP، وهي أداة متاحة في الأنظمة الشبيهة بنظام يونكس UNIX مثل ماك ولينكس وما شابههما، كما تختبر الشيفرة التالية خادمنا، حيث تستخدِم الخيار <code>‎-X</code> لتعيين تابع الطلب و<code>‎-d</code> لإدراج متن الطلب.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6243_72" style="">
<span class="pln">$ curl http</span><span class="pun">:</span><span class="com">//localhost:8000/file.txt</span><span class="pln">
</span><span class="typ">File</span><span class="pln"> not found
$ curl </span><span class="pun">-</span><span class="pln">X PUT </span><span class="pun">-</span><span class="pln">d hello http</span><span class="pun">:</span><span class="com">//localhost:8000/file.txt</span><span class="pln">
$ curl http</span><span class="pun">:</span><span class="com">//localhost:8000/file.txt</span><span class="pln">
hello
$ curl </span><span class="pun">-</span><span class="pln">X DELETE http</span><span class="pun">:</span><span class="com">//localhost:8000/file.txt</span><span class="pln">
$ curl http</span><span class="pun">:</span><span class="com">//localhost:8000/file.txt</span><span class="pln">
</span><span class="typ">File</span><span class="pln"> not found</span></pre>

<p>
	يفشل الطلب الأول إلى <code>file.txt</code> لأنّ الملف غير موجود بعد، لكن الطلب الثاني ينجح في جلب الملف بعد إنشاء طلب <code>PUT</code> لذلك الملف، ثم بعد ذلك يُفقد الملف مرةً أخرى بسبب طلب <code>DELETE</code> الذي يحذفه.
</p>

<h2>
	خاتمة
</h2>

<p>
	تسمح منصة Node لنا بتشغيل جافاسكربت في سياق خارج المتصفح، وقد صُممت أساسًا من أجل مهام الشبكات لتلعب دور عقدة -كما يشير الاسم Node- داخل شبكة ما، لكنها تتكامل جيدًا مع مهام السكربتات باختلاف أنواعها، وستستمتع بأتمتة المهام بها إذا كنت تحب جافاسكربت، كما يوفِّر NPM حزمًا لكل شيء تقريبًا ويسمح لنا بجلب وتثبيت تلك الحزم باستخدام البرنامج <code>npm</code>، كما تأتي Node بعدد من الوحدات المضمَّنة مثل وحدة <code>fs</code> التي تعمل مع نظام الملفات ووحدة <code>http</code> التي تشغّل خوادم HTTP وتنشئ طلبات HTTP أيضًا.
</p>

<p>
	تُنفَّذ جميع عمليات الإدخال والإخراج في Node بأسلوب غير متزامن إلا إذا استخدَمت نسخةً متزامنةً من الدالة صراحةً مثل <code>readFileSync</code>، كما يجب توفر دوال رد نداء عند استدعاء مثل تلك الدوال غير المتزامنة، وستستدعيها Node بقيمة خاطئة ونتيجة إذا كانت جاهزةً ومتاحةً.
</p>

<h2>
	تدريبات
</h2>

<h3>
	أداة بحث
</h3>

<p>
	توجد أداة سطر أوامر في UNIX للبحث السريع في الملفات عن تعبير نمطي وهي أداة <code>grep</code>.
</p>

<p>
	اكتب سكربت Node يمكن تشغيلها من سطر الأوامر وتتصرف مثل <code>grep</code>، بحيث تعامل أول وسيط سطر أوامر على أساس تعبير نمطي، وتعامل بقية الوسائط على أساس ملفات يجب البحث فيها، كما يجب أن يكون الخرج اسم الملف الذي يطابق محتواه التعبير النمطي، وإذا نجحت في هذا فوسِّع الأداة بحيث إذا كان أحد الوسائط مجلدًا فستبحث في جميع الملفات في ذلك المجلد ومجلداته الفرعية أيضًا.
</p>

<p>
	استخدم دوال تزامنية أو لا تزامنية وفق ما تراه مناسبًا، فرغم أنّ إعداد السكربت بحيث يمكن طلب عدة إجراءات غير متزامنة في الوقت نفسه قد يسرع البحث قليلًا، لكن ليس بالقدر الذي يكون فارقًا عن النمط التزامني بما أنّ نظم الملفات لا تستطيع قراءة أكثر من شيء واحد في كل مرة.
</p>

<h4>
	إرشادات الحل
</h4>

<p>
	ستجد الوسيط الأول لك -وهو التعبير النمطي- في <code>‎process.argv[2]‎</code>، ثم تأتي ملفات الدخل بعد ذلك، ويمكنك استخدام الباني <code>RegExp</code> للتحويل من سلسلة نصية إلى كائن تعبير نمطي، ولا شك أنّ تنفيذ هذه السكربت تزامنيًا باستخدام <code>readFileSync</code> سيكون أبسط وأسهل، لكن إذا استخدمت <code>fs.promises</code> من أجل الحصول على دوال تعيد وعودًا وكتبت دالة <code>async</code>، فلن تبدو الشيفرة غريبةً أو مختلفةً، كما يمكنك استخدام <code>stat</code> أو <code>statSync</code> والتابع <code>isDirectory</code> الخاص بكائن <code>stat</code> لمعرفة هل العنصر المبحوث عنه مجلد أم لا.
</p>

<p>
	تُعَدّ عملية تصفح مجلد عمليةً متفرعةً، حيث يمكنك تنفيذها باستخدام دالة تعاودية أو بالاحتفاظ بمصفوفة عمل -أي ملفات يجب تصفحها-.، كما تستطيع استدعاء <code>readdir</code> أو <code>readdirSync</code> للبحث عن ملفات في مجلد ما، وعليك ملاحظة أنّ أسلوب التسمية في دوال Node يختلف عن جافاسكربت وهو أقرب إلى أسلوب دوال يونكس القياسية، كما في <code>readdir</code> التي تكون كل الحروف فيها من الحالة الصغيرة، ثم نضيف <code>Sync</code> بحرف S كبير، وإذا أردت الذهاب من ملف قرأته <code>readdir</code> إلى الاسم الكامل للسمار، فيجب جمعه إلى اسم المجلد بوضع محرف شرطة مائلة <code>/</code> بينهما.
</p>

<h3>
	إنشاء المجلد
</h3>

<p>
	رغم استطاعة التابع <code>DELETE</code> الذي في خادم ملفاتنا حذف المجلدات باستخدام <code>rmdir</code> إلا أنّ الخادم لا يدعم حاليًا أي طريقة لإنشاء مجلد، لذا أضف دعمًا للتابع <code>MKCOL</code> -الذي يعني أنشئ تجميعةً Make Collection-، والذي سينشئ مجلدًا باستدعاء <code>mkdir</code> من وحدة <code>fs</code>.
</p>

<p>
	لا يُستخدم <code>MKCOL</code> -وهو تابع HTTP- كثيرًا لكنه موجود لمثل هذا الغرض تحديدًا في معيار WebDAV الذي يحدِّد مجموعةً من الأساليب فوق HTTP لتجعله مناسبًا لإنشاء المستندات.
</p>

<h4>
	إرشادات الحل
</h4>

<p>
	يمكنك استخدام الدالة التي تستخدِم التابع <code>DELETE</code> على أساس نموذج للتابع <code>MKCOL</code>، وحاول إنشاء مجلد باستخدام <code>mkdir</code> إذا لم يُعثر على ملف؛ أما إذا وجد مجلد في ذلك المسار فأعد الاستجابة 204 كي تكون طلبات إنشاء المجلدات راسخةً idempotent، فإذا وجد ملف لا يكون مجلدًا هنا فأعد رسالة خطأ، وسيكون رمز الخطأ 400 -أي "طلب سيء bad request"- هو المناسب.
</p>

<h3>
	مساحة عامة على الويب
</h3>

<p>
	بما أن خادم الملفات يتعامل مع أيّ نوع من أنواع الملفات، بل ويدرِج ترويسة <code>Content-Type</code> المناسبة، فيمكنك استخدامه لخدمة موقع ما، كما سيكون موقعًا فريدًا بما أنه يسمح لأيّ أحد بحذف الملفات واستبدالها، حيث سيكون موقعًا يمكن تعديله وتحسينه وتخريبه كذلك من قِبل أيّ أحد لديه وقت لإنشاء طلب HTTP مناسب.
</p>

<p>
	اكتب صفحة HTML تدرِج ملف جافاسكربت بسيط، وضَع الملفات في مجلد يستطيع خادم الملفات الوصول إليه ويخدمه وافتحها في المتصفح، ثم ابن واجهةً صديقةً للمستخدِم لتعديل الموقع من داخل الموقع نفسه مستفيدًا من المعلومات التي حصلتها في هذه السلسلة وعلى أساس تدريب متقدم قليلًا أو حتى على أساس مشروع لنهاية الأسبوع،.
</p>

<p>
	استخدم استمارة HTML لتعديل محتوى الملفات التي تكوّن الموقع بما يسمح للمستخدِم بتحديثها على الخادم من خلال استخدام طلبات HTTP كما ذكرنا في مقال <a href="https://academy.hsoub.com/programming/javascript/http-%D9%88%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1367/" rel="">HTTP والاستمارات في جافاسكربت</a>، وابدء بجعل ملف واحد فقط قابلًا للتعديل ثم أكثر من ملف بحيث يستطيع المستخدِم اختيار أيّ ملف يمكن تعديله، واستفد من كون خادم الملفات يعيد قائمةً من الملفات عند قراءة مجلد ما، كما لا تعمل في الشيفرة المعروضة لخادم الملفات مباشرةً بما أنك قد تخطئ فتدمِّر الملفات التي هناك، بل اجعل عملك خارج المجلد العام وانسخه عند الاختبار.
</p>

<h4>
	إرشادات الحل
</h4>

<p>
	تستطيع إنشاء عنصر <code>&lt;textarea&gt;</code> لحفظ محتوى الملف الذي يُعدَّل، ويمكن جلب محتوى الملف الحالي باستخدام <code>GET</code> الذي يستخدِم <code>fetch</code>، كما تستطيع استخدام الروابط النسبية مثل index.html بدلًا من <a href="http://localhost:8000/index.html" ipsnoembed="false" rel="external nofollow">http://localhost:8000/index.html</a> للإشارة إلى الملفات التي على الخادم نفسه الذي عليه السكربت العاملة، وإذا نقر المستخدِم على زر ما -حيث يمكنك استخدام العنصر <code>&lt;form&gt;</code> والحدث <code>"submit"</code> لذلك- فأنشئ طلب <code>PUT</code> إلى الرابط نفسه بمحتوى <code>&lt;textarea&gt;</code> على أساس متن للطلب من أجل حفظ الملف.
</p>

<p>
	يمكنك بعد ذلك إضافة العنصر <code>&lt;select&gt;</code> الذي يحتوي على جميع الملفات في المجلد الأعلى للخادم بإضافة عناصر <code>&lt;option&gt;</code> التي تحتوي الأسطر المعادة بواسطة طلب <code>GET</code> إلى الرابط <code>/</code>، وإذا اختار المستخدِم ملفًا آخرًا -أي الحدث <code>"change"</code> على ذلك الملف-، فيجب على السكربت جلب ذلك الملف وعرضه، ومن أجل حفظ ملف ما استخدِم اسم الملف المحدد حاليًا.
</p>

<p>
	ترجمة -بتصرف- <a href="https://eloquentjavascript.net/20_node.html" rel="external nofollow">للفصل العشرين من كتاب Elequent Javascript</a> لصاحبه Marijn Haverbeke.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%A5%D9%86%D8%AC%D8%A7%D8%B2-%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D9%85%D8%AD%D8%B1%D8%B1-%D8%B1%D8%B3%D9%88%D9%85-%D9%86%D9%82%D8%B7%D9%8A%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1401/" rel="">إنجاز مشروع محرر رسوم نقطية باستخدام جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/%d9%83%d9%8a%d9%81%d9%8a%d8%a9-%d8%a7%d8%b3%d8%aa%d9%83%d8%b4%d8%a7%d9%81-%d9%88%d8%a5%d8%b5%d9%84%d8%a7%d8%ad-%d8%b1%d9%85%d9%88%d8%b2-%d8%a3%d8%ae%d8%b7%d8%a7%d8%a1-http-%d8%a7%d9%84%d8%b4%d8%a7%d8%a6%d8%b9%d8%a9-r116/" rel="">كيفية استكشاف وإصلاح رموز أخطاء HTTP الشائعة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D9%81%D8%B9%D8%A7%D9%84-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%A7%D9%84%D8%A7%D9%81%D8%AA%D8%B1%D8%A7%D8%B6%D9%8A%D8%A9-%D9%84%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D9%88%D8%B6%D8%A8%D8%B7%D9%87%D8%A7-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1143/" rel="">أفعال المتصفح الافتراضية للأحداث وضبطها عبر جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%86%D9%88%D8%A7%D9%81%D8%B0-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D9%88%D8%A7%D9%84%D9%86%D9%88%D8%A7%D9%81%D8%B0-%D8%A7%D9%84%D9%85%D9%86%D8%A8%D8%AB%D9%82%D8%A9-popups-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1253/" rel="">التعامل مع نوافذ المتصفح والنوافذ المنبثقة Popups في جافاسكربت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1402</guid><pubDate>Fri, 17 Dec 2021 09:11:01 +0000</pubDate></item></channel></rss>
