إضافة التفاعل باستخدام JavaScript

تتيح لنا لغة JavaScript تعديل جميع جوانب الصفحة، مثل المحتوى والنمط واستجابته لتفاعل المستخدم. ومع ذلك، يمكن لـ JavaScript أيضًا حظر إنشاء DOM وتأخير عرض الصفحة. لتقديم أفضل أداء، اجعل JavaScript غير متزامن وأزِل أي محتوى JavaScript غير ضروري من مسار العرض الحرج.

ملخّص

  • يمكن لـ JavaScript الاستعلام عن DOM وCSSOM وتعديلهما.
  • عمليات حظر تنفيذ JavaScript في CSSOM.
  • تحظر JavaScript إنشاء DOM ما لم يتم الإعلان صراحةً على أنّه غير متزامن.

لغة JavaScript هي لغة ديناميكية تعمل في متصفّح وتتيح لنا تغيير كل جانب من جوانب سلوك الصفحة: يمكننا تعديل المحتوى عن طريق إضافة عناصر وإزالتها من شجرة DOM، كما يمكننا تعديل خصائص CSSOM لكل عنصر، كما يمكننا معالجة إدخالات المستخدم وغير ذلك الكثير. لتوضيح ذلك، دعنا نزيد مثالنا السابق "Hello World" باستخدام نص برمجي مضمّن بسيط:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script>
      var span = document.getElementsByTagName('span')[0];
      span.textContent = 'interactive'; // change DOM text content
      span.style.display = 'inline'; // change CSSOM property
      // create a new element, style it, and append it to the DOM
      var loadTime = document.createElement('div');
      loadTime.textContent = 'You loaded this page on: ' + new Date();
      loadTime.style.color = 'blue';
      document.body.appendChild(loadTime);
    </script>
  </body>
</html>

التجربة الآن

  • تتيح لنا لغة JavaScript الوصول إلى نموذج العناصر في المستند (DOM) وسحب المرجع إلى عقدة النطاق المخفية، وقد لا تكون العقدة مرئية في شجرة العرض، ولكنها لا تزال موجودة في نموذج العناصر في المستند (DOM). وبعد ذلك، عندما يكون لدينا المرجع، يمكننا تغيير نصه (عبر .textContent)، وحتى إلغاء خاصية نمط العرض المحسوب من "بلا" إلى "مضمّن". تعرض صفحتنا الآن "مرحبًا بالطلاب المتفاعلين.".

  • كما يتيح لنا JavaScript إنشاء عناصر جديدة ونمطها وإلحاقها وإزالتها في DOM. ومن الناحية الفنية، يمكن أن تكون الصفحة بأكملها عبارة عن ملف JavaScript واحد كبير ينشئ العناصر ويصمّمها واحدًا تلو الآخر. على الرغم من أن ذلك قد ينجح، إلا أن استخدام HTML وCSS يعد أسهل بكثير. في الجزء الثاني من دالة JavaScript، ننشئ عنصر div جديدًا ونضبط المحتوى النصي الخاص به ونضبطه ونلحقه بالنص.

معاينة الصفحة

بذلك، عدَّلنا المحتوى ونمط CSS لعقدة DOM حالية وأضفنا عقدة جديدة تمامًا إلى المستند. لن تفوز صفحتنا بأي جوائز تصميم، ولكنها توضح القوة والمرونة التي تتيح لنا JavaScript.

وبالرغم من ذلك، تمنحنا لغة JavaScript قدرًا كبيرًا من القوة، إلا أنّها تفرض الكثير من القيود الإضافية على كيفية ووقت عرض الصفحة.

أولاً، لاحظ أنّ النص البرمجي المضمّن في المثال أعلاه يقع بالقرب من أسفل الصفحة. ولماذا؟ حسنًا، يجب أن تجرِّب ذلك بنفسك، ولكن إذا نقلنا النص البرمجي أعلى عنصر span، ستلاحظ أن النص البرمجي قد فشله وتشتكي من أنه لا يمكنه العثور على إشارة إلى أي عناصر span في المستند؛ وهذا يعني أن getElementsByTagName(‘span') يعرض null. وهذا يوضح خاصية مهمة: يتم تنفيذ النص البرمجي في الوقت الذي تم إدراجه فيه في المستند بالضبط. عندما يصادف محلل HTML علامة نص برمجي، فإنه يوقف عملية إنشاء DOM مؤقتًا وينتج عنه تحكم لمحرك بحث جافا سكريبت؛ وبعد انتهاء تشغيل محرك جافا سكريبت، ينتقل المتصفح من حيث توقف ويستأنف إنشاء نموذج العناصر في المستند (DOM).

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

ومن الخصائص الدقيقة الأخرى لإدخال نصوص برمجية في صفحتنا توفير إمكانية قراءة وتعديل عناصر DOM وCSSOM أيضًا. في الواقع، هذا ما نفعله في المثال عند تغيير خاصية عرض عنصر الامتداد من بلا إلى تضمين. النتيجة النهائية؟ لدينا الآن شرط سباق.

ماذا لو لم ينتهِ المتصفِّح من تنزيل وإنشاء CSSOM عندما نريد تشغيل النص البرمجي؟ الإجابة بسيطة وليست جيدة جدًا بالنسبة إلى الأداء: يؤخّر المتصفّح تنفيذ النص البرمجي وإنشاء نموذج العناصر في المستند (DOM) إلى أن ينتهي من تنزيل وإنشاء CSSOM.

باختصار، تقدّم JavaScript الكثير من التبعيات الجديدة بين تنفيذ عناصر DOM وCSSOM وJavaScript. ويمكن أن يؤدي ذلك إلى حدوث تأخيرات كبيرة في المتصفح أثناء معالجة الصفحة وعرضها على الشاشة:

  • موقع النص البرمجي في المستند مهم.
  • عندما يصادف المتصفِّح علامة نص برمجي، يتوقف إنشاء DOM مؤقتًا إلى أن ينتهي تنفيذ النص البرمجي.
  • يمكن لـ JavaScript الاستعلام عن DOM وCSSOM وتعديلهما.
  • تتوقف عملية تنفيذ JavaScript مؤقتًا إلى أن تصبح ملفات CSSOM جاهزة.

يشير مصطلح "تحسين مسار العرض الحرج" بدرجة كبيرة إلى فهم وتحسين الرسم البياني للتبعية بين HTML وCSS وJavaScript.

حظر المحلل اللغوي مقابل JavaScript غير المتزامن

يتم تنفيذ JavaScript تلقائيًا عند "حظر المحلل اللغوي": عندما يصادف المتصفح نصًا برمجيًا في المستند، يجب أن يتوقف عن إنشاء DOM مؤقتًا، ويسلم التحكم في وقت تشغيل JavaScript، ويترك النص البرمجي قيد التنفيذ قبل متابعة إنشاء نموذج العناصر في المستند (DOM). رأينا ذلك عمليًا باستخدام نص برمجي مضمّن في المثال السابق. وفي الواقع، تحظر النصوص البرمجية المضمّنة دائمًا المحلل اللغوي ما لم تكتب رمزًا إضافيًا لتأجيل تنفيذها.

ماذا عن النصوص البرمجية المضمّنة عبر علامة نص برمجي؟ لنأخذ مثالنا السابق ونستخرج التعليمة البرمجية في ملف منفصل:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script External</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js"></script>
  </body>
</html>

app.js

var span = document.getElementsByTagName('span')[0];
span.textContent = 'interactive'; // change DOM text content
span.style.display = 'inline'; // change CSSOM property
// create a new element, style it, and append it to the DOM
var loadTime = document.createElement('div');
loadTime.textContent = 'You loaded this page on: ' + new Date();
loadTime.style.color = 'blue';
document.body.appendChild(loadTime);

التجربة الآن

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

ويحظر المحلل اللغوي تلقائيًا كل ملفات JavaScript. ونظرًا لأن المتصفح لا يعرف ما يخطط النص البرمجي لفعله في الصفحة، فإنه يفترض السيناريو الأسوأ ويحظر المحلل اللغوي. تشير إشارة إلى المتصفِّح إلى أنّه لا حاجة إلى تنفيذ النص البرمجي في النقطة التي تمت الإشارة إليها فيها بالضبط، وهي تسمح للمتصفح بمواصلة إنشاء نموذج العناصر في المستند (DOM) والسماح بتنفيذ النص البرمجي عندما يكون جاهزًا، على سبيل المثال، بعد جلب الملف من ذاكرة التخزين المؤقت أو خادم بعيد.

ولتحقيق ذلك، نضع علامة على النص البرمجي على أنّه async:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script Async</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js" async></script>
  </body>
</html>

التجربة الآن

تؤدي إضافة الكلمة الرئيسية غير المتزامنة إلى علامة النص البرمجي إلى إعلام المتصفح بعدم حظر إنشاء نموذج العناصر في المستند (DOM) أثناء انتظاره توفر النص البرمجي، ما قد يؤدي إلى تحسين الأداء بشكل كبير.

إضافة ملاحظات