איך נשפר את ה- caching לדפים סטטים
אתם מפתחים אתר אינטרנט ואתם רוצים לשפר בו ביצועים?
נראה לכם שכבר עשיתם הכל?
הנה עוד דבר קטן שאפשר לנסות...
caching לדפים סטטים (JS, CSS, Images וכו').
[אני אדבר על קבצי JS, אך הכל נכון גם לסוגי הדפים האחרים]
כברירת מחדל, הדפדפן שלכם מבצע caching באופן הבא:
בכל פעם שהוא נדרש "להוריד" דף מהשרת, הוא בודק האם הדף קיים ב- cache המקומי שלו.
הבדיקה מתבצעת עבור כל ה- url , כולל הפרמטרים.
אם הדף לא נמצא ב- cache, הדפדפן ניגש לשרת ו"מבקש" את הדף.
אם הדף כן נמצאב-cache, הדפדפן ניגש לשרת ו"מבקש" את הדף ומוסיף ב – header של הבקשה "שאלה": If-Modified-Since: dd/mm/yyyy hh:MM:ss.
אם השרת מזהה שהדף הקיים אצלו לא שונה מאז התאריך הנקוב ב- request הוא לא מחזיר את הדף המבוקש, אלא מחזיר לדפדפן סטטוס 304(Not Modified). הדפדפן "מבין" שהדף שקיים אצלו ב- cache עדיין עדכני ומשתמש בו.
כך שבעצם, גם אם הדפים הסטטים שלי נמצאים ב- cache, כברירת מחדל, מתבצעת גישה לשרת לבדוק האם עדיין מעודכנים.
אז מה אפשר לעשות?
בואו נראה איך אי יכול "לחסוך" את הגישות המיותרות לשרת...
כאשר אני בונה את האתר אני אגדיר ספריית Scripts שבה אמקם את קבצי ה - JS שלי.
בשרת ה - IIS אני יכול הגדיר לספרייה content expiration. יש 2 אפשרויות:
לפי תאריך – כך, כאשר הדף ירד לדפדפן הוא ירד עם הנתון של "עד איזה תאריך הקובץ יכול להיות ב - cache", כך שעד תאריך זה, הדפדפן לא יגש לשרת לבקש את הדף, אלא יקח אותו ישירות מה - cache. לאחר תאריך זה, הדפדפן יחזור להתנהגות ברירת המחדל של בדיקת cahce עם סטטוס 304.
החסרון של ההגדרה הזו הוא שקשה להגדיר תאריך, אלא אם יודעים מתי הגרסה הבאה מתוכננת. בנוסף לכך זה דורש כל פעם לגשת לשרת ה - IIS ולעדכן את התאריך.
אפשרות שנייה היא לפי קבוע זמן - מספר ימים, והשרת מתרגם זאת לתאריך עתידי. כך, כאשר הדף יורד מהשרת לדפדפן הוא יורד עם "תאריך תוקף" עתידי וכל עוד הנתון בתוקף הדפדפן ייקח את הדף מה - cache ללא פניות לשרת. לאחר שיעברו מספר הימים שהוגדרו, הדפדפן יפנה שוב לשרת ויוריד מחדש דף עם "תאריך תוקף" שחושב מחדש לפי מספר הימים המוגדר. כך שאם הגדרנו 5 ימים, כל 5 ימים הדפדפן ייגש לשרת להוריד דף חדש.
החיסרון של הגדרה זו הוא שקשה לקבוע מספר ימים כזה שתמיד יפוג ביום של העלאת גרסה חדשה.
אז מה עוד אפשר לעשות?
אחת השיטות המקובלות היא כתיבת url של דף עם פרמטר שהוא מספר הגרסה של המערכת:
<script src="myscript.js?v=1.4.6"/>
בנוסף לכך, הגדרת התאריך ב - content expiration לכמה שנים קדימה, כך שבעצם ה- url תמיד יילקח מה - cache.
וכאשר מעלים גרסה חדשה של האתר, ה - url ישתנה (מכיוון שהוא כולל מספר גרסה) וכך הדפדפן ייגש לשרת לקחת דף חדש, שגם הוא יכנס ל - cache עד קץ הדורות
החסרונות של פתרון זה הם לפחות 2:
- אנחנו "מנפחים" את ה - cache של הדפדפן.
- האם כל פעם כשמעלים גרסה נעבור על כל ה - urls ונעדכן את מספר הגרסה?
אז ככה, לגבי הבעיה הראשונה, במידה מסוימת אפשר להתנהג כבת יענה (לטמון ראשינו בחול).
לגבי הבעיה השנייה:
ניתן לייצר את הגדרת ה - Script ע"י הפונקציה RegisterClientScriptInclude() מתוך דף ה - aspx, כך בכל פעם תחולל תגית ה- script מחדש עם ה - url ומספר הגרסה הנוכחי. דוגמא:
Page.ClientScript.RegisterClientScriptInclude("myjscriptfilekey", "/myscript.js?v="+GetVersion());
אפשרות נוספת היא הגדרת server control שמייצר את תגית ה - script.
היתרון בפתרון זה הוא שהוא נותן למתכנת תחושה שהוא רגיל אליה - כתיבת תגית ה- script בתוך ה - Head בעצמו, אך מכיוון שזהו server control התגית תחולל כל פעם מחדש עם הגרסה העדכנית.
דוגמא ל – server control:
public class Script : Control
{
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "Execution")]
protected override void Render(HtmlTextWriter output)
{
Version v = GetMyApplicationVersion();
output.AddAttribute(HtmlTextWriterAttribute.Src, this.Url+"?v="+v.ToString());
output.AddAttribute("type", "text/javascript");
output.RenderBeginTag(HtmlTextWriterTag.Script);
output.RenderEndTag();
output.Flush();
}
[UrlProperty()]
public string Url
{
get
{
String url = (String)ViewState["Url"];
return ((url == null) ? String.Empty : url);
}
set
{
ViewState["Url"] = value;
}
}
}
וכך נשתמש בו בתוך דף ה - aspx:
<mcs:Script Url="MyScript.js" runat="server"/>
וזה מה שירונדר:
<script src="MyScript.js?v=1.0.3630.23231" type="text/javascript"></script>
יש לכם רעיונות נוספים/אחרים איך לשפר את הביצועים?
בהצלחה,
טל