רקע: קצת על Bundling & Minification

    מה זה בכלל?

    Bundling ו-Minification (להלן B&M) הן שתי שיטות להאצה של טעינת דפי אינטרנט שמובנות ב-ASP.NET:

    Bundling (או “כריכה” בעברית. בהמשך אשתמש במונח העברי, כשלא יהיה לי כח להחליף את שפת הכתיבה לאנגלית) היא טכניקה שבה מחברים מספר קבצים וכורכים אותם יחד לקובץ אחד ארוך.

    Minification (או “כיווץ”. כנ”ל) היא שיטה להקטנת נפח של קבצים, באמצעות מחיקת הערות, רווחים וירידת שורות ועל ידי שינוי שמות של פונקציות ומשתנים פנימיים.

    לדוגמה, נניח שיש לנו בפרויקט את שתי הקבצים הבאים:

    Code Snippet
    1. //calc.js
    2. var calc = (function () {
    3.  
    4.     //Return sum of two numbers
    5.     //params:
    6.     //firstNumber – Number
    7.     //secondNumber – Number
    8.     function sum2Numbers(firstNumber, secondNumber) {
    9.         return firstNumber + secondNumber;
    10.     }
    11.     
    12.     return {
    13.         sum: sum2Numbers
    14.     };
    15.  
    16. })();

     

    Code Snippet
    1. //answer.js
    2. function answer() {
    3.     return calc.sum(7, 35);
    4. }

    התוצאה של תהליך הכריכה והכיווץ היא זו:

    Code Snippet
    function answer(){return calc.sum(7,35)}var calc=function(){function n(n,t){return n+t}return{sum:n}}()

    ניתן לשים לב ששתי הקבצים נכרכו יחד, והוסרו מהם הערות, רווחים, ירידות שורה ואפילו סימני נקודה-פסיק(;) שאינם נחוצים. בנוסף הפונקציה sum2Numbers נקראת עתה n והפרמטרים שלה נקראים n, t.

    מה טוב בזה?

    • היתרון הראשון הוא כמובן הקטנת נפח הקבצים. בדוגמה למעלה זה כמובן לא משמעותי, אבל בקבצים גדולים זה עשוי להיות הבדל ענק (לדוגמה, הנפח של אנגולר 1.5.0 הוא יותר מ- 1,100 KB, לעומת זאת גרסת ה-min שלו היא רק 151 KB! הבדל של כמעט מגה-בייט).
    • יתרון שני, ולפעמים אפילו יותר חשוב, הקטנת מספר הקריאות. דפדפנים מודרניים מגבילים את מספר החיבורים שניתן לפתוח בו זמנית לשרת אחד (בדרך כלל ל-6). וזה אומר שאם יש הרבה קבצי JavaScript קטנים, הבקשה לחלק מהם תישלח רק אחרי שהקבצים הראשונים יסיימו לרדת. לעומת זאת כאשר נעשית כריכה של הקבצים הם יורדים, כולם יחד, בקריאה אחת.
    • דבר נוסף שטוב ב-B&M הוא הקלות של ניהול המטמון (Cache). דפדפנים שומרים במטמון את הקבצים האחרונים שהם פנו אליהם, וכשהם צריכים אותם שוב הם לא נגשים לרשת, אלא שולפים אותם מהמטמון. הבעיה היא שלפעמים, אחרי שינוי שנעשה בקוד והועלה לשרת, אצל הלקוח עדיין מגיעה הגירסה הישנה מהמטמון.
      כאשר משתמשים בכריכה נוצר קובץ אחד שהגישה אליו היא עם מזהה (Token) לדוגמה: www.somesite.com/scriptsBundle?v=v4IitxZGb_-XBJ7UwxUiU3Gmttdk_XIDqm91mfORWNk1.  אם אחד הקבצים ישתנה והם יכרכו מחדש, המזהה יוחלף, ומבחינת הדפדפן זהו קובץ אחר ותמיד הוא יקרא אותו שוב.
    • רווח צדדי נוסף הוא ערבול (scrambling) הקוד. לפעמים אנחנו לא רוצים שבצד הלקוח יסתכלו, יעתיקו ו/או ישנו את הקוד שלנו. הכיווץ נותן לנו הגנה מסויימת מפני זה (חשוב להדגיש שזה לא תחליף לכלי scrambling יעודי במידה שיש צורך, זה קו הגנה ראשון בלבד).

    קריאה נוספת: http://www.asp.net/mvc/overview/performance/bundling-and-minification

    התאמה אישית של התהליך

    כאשר מכריזים על אובייקט מסוג Bundle (או ממחלקה שיורשת ממנו, כמו StyleBundle ו-ScriptBundle) מתקבל אובייקט שבו (בין השאר) מאפיין Transforms שהוא רשימה של IBundleTransform. במהלך תהליך ה-B&M כל אחד מה-IBundleTransform שברשימה מופעל והתוצאה עוברת לבא בתור.

    באמצעות כתיבת IBundleTransform משלנו, אפשר להתערב בתהליך, ולהחליף את הממירים הרגילים (JsMinify ו-CssMinify) או להוסיף עליהם.

    הבה ונראה דוגמה פשוטה:

    Code Snippet
    1. public class BundleConfig
    2. {
    3.     public static void RegisterBundles(BundleCollection bundles)
    4.     {
    5.         Bundle bundle = new ScriptBundle(“~/scriptsBundle”)
    6.             .Include(“~/content/calc.js”)
    7.             .Include(“~/content/answer.js”);
    8.         bundle.Transforms.Add(new CopyrightTransform());
    9.         bundles.Add(bundle);
    10.     }
    11. }
    12.  
    13. public class CopyrightTransform : IBundleTransform
    14. {
    15.     public void Process(BundleContext context, BundleResponse response)
    16.     {
    17.         string copyrightMsg = string.Format(“Copyright © {0} HaravDotNet ltd. All rights reserved.”, DateTime.Now.Year);
    18.         string createdAt = string.Format(“Created & bundeld at {0}.”, DateTime.Now);
    19.         response.Content = String.Format(“/* {0} */\n/* {1} */\n{2}”, copyrightMsg, createdAt, response.Content);
    20.     }
    21. }

    אז מה עשינו פה?

    • (שורה 13) הגדרנו מחלקה חדשה שנקראת CopyrightTransform ומממשת את הממשק IBundleTransform.
    • (שורה 15) המחלקה מממשת את השיטה Process שנקראת כאשר מתבצע תהליך ה-B&M.
    • (שורות 17-19) השיטה מוסיפה לתוצאה הקיימת (אני מניח שזה נקרא רק לאחר ש-JsMinify כבר התבצע) שורת Copyright ואת זמן היצירה שלו.
    • (שורה 5) אני יוצר ScriptBundle ומוסיף לו את הקבצים שלי. כרגע ברשימת ה-Transforms שלו יש רק אחד, JsMinify.
    • (שורה 8) אני מוסיף לרשימת ה-Transforms מופע של CopyrightTransform, הוא נוסף לאחר JsMinify, וירוץ אחריו.

    וכך נראה הקובץ שנוצר בסיום התהליך.

    Code Snippet
    1. /* Copyright © 2016 HaravDotNet ltd. All rights reserved. */
    2. /* Created & bundeld at 2/29/2016 9:53:30 AM. */
    3. function answer(){return calc.sum(7,35)}var calc=function(){function n(n,t){return n+t}return{sum:n}}()

    סיכום ביניים

    למדנו בפרק הזה על כריכה ועל כיווץ, מה הם עושים ומה היתרונות שלהם. למדנו קצת גם על התאמה אישית של התהליך, וראינו דוגמה פשוטה להוספת שלב בסוף התהליך.

    מה הלאה? בפרק הבא נראה, בעזרת השם, שימוש יותר מעשי ב-Bundling מותאם אישית.