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 ולשלוף את ההפניה לצומת ה-span המוסתר. ייתכן שהצומת לא יהיה גלוי בעץ העיבוד, אבל הוא עדיין נמצא ב-DOM. לאחר מכן, אחרי שקובץ העזר נמצא, נוכל לשנות את הטקסט שלו (דרך .textContent), ואפילו לשנות את מאפיין סגנון התצוגה המחושב מ-'none' ל-'inline'. עכשיו בדף שלנו מוצג הכיתוב 'שלום לתלמידים אינטראקטיביים!'.
כמו כן, JavaScript מאפשר לנו ליצור, לעצב, להוסיף ולהסיר רכיבים חדשים ב-DOM. מבחינה טכנית, הדף כולו יכול להיות רק קובץ JavaScript גדול אחד שיוצר ומעצב את הרכיבים אחד אחרי השני. למרות שזה יעבוד, קל יותר להשתמש ב-HTML וב-CSS. בחלק השני של פונקציית ה-JavaScript, אנחנו יוצרים רכיב div חדש, מגדירים את תוכן הטקסט שלו, מעצבים אותו ומצרפים אותו לגוף.
שינינו את התוכן ואת סגנון ה-CSS של צומת DOM קיים, והוספנו צומת חדש לגמרי למסמך. הדף שלנו לא יזכה בפרסי עיצוב, אבל הוא ממחיש את העוצמה והגמישות ש-JavaScript מעניק לנו.
עם זאת, אמנם JavaScript מאפשר לנו כוח רב, אך הוא יוצר מגבלות נוספות על אופן עיבוד הדף והמועד שלו.
תחילה, שימו לב שהסקריפט המוטבע שלנו בדוגמה שלמעלה נמצא קרוב לתחתית הדף. למה? כדאי לכם לנסות בעצמכם, אבל אם נעביר את הסקריפט מעל לאלמנט span, תראו שהסקריפט ייכשל ותתלונן שהוא לא יכול למצוא הפניה לרכיבי span במסמך; כלומר, getElementsByTagName('span') מחזיר null. זה מדגים מאפיין חשוב: הסקריפט שלנו מבוצע בדיוק בנקודה שבה הוא מוכנס למסמך. כשמנתח HTML נתקל בתג סקריפט, הוא משהה את תהליך בניית ה-DOM ונותן את השליטה למנוע ה-JavaScript. לאחר שמנוע ה-JavaScript מסיים לפעול, הדפדפן ממשיך מהמקום שבו הפסיק וממשיך בבניית ה-DOM.
במילים אחרות, בלוק הסקריפטים שלנו לא מצליח למצוא רכיבים כלשהם בשלב מאוחר יותר בדף מכיוון שהם עדיין לא עברו עיבוד! או לנסח אחרת: הפעלת הסקריפט המוטבע חוסם את בניית ה-DOM, ולכן גם מעכב את העיבוד הראשוני.
תכונה עדינה נוספת של הכנסת סקריפטים לדף שלנו היא שהם יכולים לקרוא ולשנות לא רק את ה-DOM, אלא גם את מאפייני CSSOM. למעשה, זה בדיוק מה שאנחנו עושים בדוגמה שלנו כשמשנים את מאפיין התצוגה של רכיב span מ-None ל-inline. התוצאה הסופית? עכשיו יש לנו תנאי מרוץ.
מה קורה אם הדפדפן לא מסיים להוריד ולבנות את ה-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 בזמן שהוא ממתין להפיכת הסקריפט לזמין, וכתוצאה מכך ניתן לשפר משמעותית את הביצועים.