افزودن تعامل با جاوا اسکریپت

جاوا اسکریپت به ما امکان می دهد تقریباً هر جنبه ای از صفحه را تغییر دهیم: محتوا، سبک و پاسخ آن به تعامل با کاربر. با این حال، جاوا اسکریپت همچنین می تواند ساخت DOM را مسدود کند و هنگام رندر شدن صفحه به تاخیر بیاندازد. برای ارائه عملکرد بهینه، جاوا اسکریپت خود را ناهمگام کنید و هر جاوا اسکریپت غیر ضروری را از مسیر رندر حیاتی حذف کنید.

خلاصه

  • جاوا اسکریپت می تواند DOM و CSSOM را پرس و جو کرده و تغییر دهد.
  • بلوک های اجرای جاوا اسکریپت در CSSOM.
  • جاوا اسکریپت ساخت DOM را مسدود می کند مگر اینکه به طور صریح به عنوان ناهمگام اعلام شود.

جاوا اسکریپت یک زبان پویا است که در یک مرورگر اجرا می شود و به ما امکان می دهد تقریباً هر جنبه ای از نحوه رفتار صفحه را تغییر دهیم: می توانیم با افزودن و حذف عناصر از درخت 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>

آن را امتحان کنید

  • جاوا اسکریپت به ما اجازه می دهد تا به DOM دسترسی پیدا کرده و ارجاع به گره span پنهان را بیرون بکشیم. گره ممکن است در درخت رندر قابل مشاهده نباشد، اما همچنان در DOM وجود دارد. سپس، وقتی مرجع را داریم، می‌توانیم متن آن را تغییر دهیم (از طریق .textContent)، و حتی ویژگی سبک نمایش محاسبه‌شده آن را از "none" به "inline" لغو کنیم. اکنون صفحه ما " سلام دانش آموزان تعاملی! " را نمایش می دهد.

  • جاوا اسکریپت همچنین به ما اجازه می دهد تا عناصر جدید را در DOM ایجاد، استایل، الحاق و حذف کنیم. از نظر فنی، کل صفحه ما می تواند فقط یک فایل جاوا اسکریپت بزرگ باشد که عناصر را یکی یکی ایجاد و سبک می کند. اگرچه این کار می کند، اما در عمل استفاده از HTML و CSS بسیار ساده تر است. در بخش دوم تابع جاوا اسکریپت ما یک عنصر div جدید ایجاد می کنیم، محتوای متن آن را تنظیم می کنیم، آن را استایل می دهیم و به بدنه اضافه می کنیم.

پیش نمایش صفحه

با آن، محتوا و سبک CSS یک گره DOM موجود را تغییر دادیم و یک گره کاملاً جدید به سند اضافه کردیم. صفحه ما برنده هیچ جایزه طراحی نمی شود، اما قدرت و انعطاف پذیری جاوا اسکریپت را به ما نشان می دهد.

با این حال، در حالی که جاوا اسکریپت به ما قدرت زیادی می دهد، محدودیت های زیادی در نحوه و زمان ارائه صفحه ایجاد می کند.

ابتدا توجه کنید که در مثال بالا اسکریپت درون خطی ما نزدیک به انتهای صفحه است. چرا؟ خوب، شما باید خودتان آن را امتحان کنید، اما اگر اسکریپت را به بالای عنصر span منتقل کنیم، متوجه خواهید شد که اسکریپت از کار می افتد و شکایت می کند که نمی تواند مرجعی به هیچ عنصر span در سند پیدا کند. یعنی getElementsByTagName('span') null را برمی‌گرداند. این یک ویژگی مهم را نشان می دهد: اسکریپت ما دقیقاً در نقطه ای که در سند درج شده است اجرا می شود. هنگامی که تجزیه کننده HTML با یک تگ اسکریپت مواجه می شود، فرآیند ساخت DOM را متوقف می کند و کنترل را به موتور جاوا اسکریپت می دهد. پس از اتمام کار موتور جاوا اسکریپت، مرورگر از جایی که کار را متوقف کرده است ادامه می دهد و ساخت DOM را از سر می گیرد.

به عبارت دیگر، بلوک اسکریپت ما نمی تواند هیچ عنصری را بعداً در صفحه پیدا کند زیرا هنوز پردازش نشده است! یا به عبارت کمی متفاوت تر: اجرای اسکریپت درون خطی ما ساخت DOM را مسدود می کند، که رندر اولیه را نیز به تاخیر می اندازد.

یکی دیگر از ویژگی های ظریف معرفی اسکریپت ها به صفحه ما این است که می توانند نه تنها DOM، بلکه ویژگی های CSSOM را نیز بخوانند و تغییر دهند. در واقع، این دقیقاً همان کاری است که ما در مثال خود انجام می دهیم، زمانی که ویژگی نمایش عنصر span را از none به inline تغییر می دهیم. نتیجه نهایی؟ اکنون شرایط مسابقه داریم.

اگر مرورگر دانلود و ساخت CSSOM را تمام نکرده باشد، وقتی که ما می خواهیم اسکریپت خود را اجرا کنیم، چه؟ پاسخ ساده است و برای عملکرد خیلی خوب نیست: مرورگر اجرای اسکریپت و ساخت DOM را تا پایان دانلود و ساخت CSSOM به تاخیر می اندازد.

به طور خلاصه، جاوا اسکریپت وابستگی های جدید زیادی را بین اجرای DOM، CSSOM و جاوا اسکریپت معرفی می کند. این می تواند باعث تاخیر قابل توجه مرورگر در پردازش و رندر صفحه روی صفحه شود:

  • مکان اسکریپت در سند قابل توجه است.
  • هنگامی که مرورگر با یک تگ اسکریپت روبرو می شود، ساخت DOM تا زمانی که اجرای اسکریپت به پایان برسد متوقف می شود.
  • جاوا اسکریپت می تواند DOM و CSSOM را پرس و جو کرده و تغییر دهد.
  • اجرای جاوا اسکریپت تا زمانی که CSSOM آماده شود متوقف می شود.

تا حد زیادی، "بهینه سازی مسیر رندر بحرانی" به درک و بهینه سازی نمودار وابستگی بین HTML، CSS و جاوا اسکریپت اشاره دارد.

مسدود کردن تجزیه کننده در مقابل جاوا اسکریپت ناهمزمان

به‌طور پیش‌فرض، اجرای جاوا اسکریپت «مسدود تجزیه‌کننده» است: وقتی مرورگر با یک اسکریپت در سند مواجه می‌شود، باید ساخت DOM را متوقف کند، کنترل را به زمان اجرا جاوا اسکریپت واگذار کند و اجازه دهد اسکریپت قبل از ادامه ساخت 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> یا یک قطعه جاوا اسکریپت درون خطی استفاده کنیم، انتظار دارید هر دو به یک شکل عمل کنند. در هر دو مورد، مرورگر قبل از اینکه بتواند بقیه سند را پردازش کند، اسکریپت را متوقف کرده و اجرا می کند. با این حال، در مورد یک فایل جاوا اسکریپت خارجی، مرورگر باید مکث کند تا منتظر بماند تا اسکریپت از دیسک، حافظه پنهان یا یک سرور راه دور واکشی شود، که می تواند ده ها تا هزاران میلی ثانیه تاخیر را به مسیر رندر بحرانی اضافه کند.

به طور پیش فرض تمام جاوا اسکریپت مسدود کننده تجزیه کننده است. از آنجایی که مرورگر نمی داند اسکریپت قصد دارد چه کاری را در صفحه انجام دهد، بدترین حالت را در نظر گرفته و تجزیه کننده را مسدود می کند. یک سیگنال به مرورگر مبنی بر اینکه اسکریپت نیازی به اجرای دقیق در نقطه ای که به آن ارجاع داده نمی شود، به مرورگر اجازه می دهد تا به ساختن 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 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>

آن را امتحان کنید

افزودن کلمه کلیدی async به تگ اسکریپت به مرورگر می‌گوید که ساخت DOM را تا زمانی که منتظر در دسترس قرار گرفتن اسکریپت است مسدود نکند، که می‌تواند عملکرد را به میزان قابل توجهی بهبود بخشد.

بازخورد