יום שבת, 17 בנובמבר 2012

הצצה מהירה על Backbone.js (ספריית MVC)

שפת ג׳אווהסקריפט התפתחה בעשור האחרון מ״שפת צעצוע״, המשמשת ליצירת אינטראקציה מקומית בדף אינטרנט, לשפה משמעותית שמשמשת לבניית מערכות שלמות. ממה נבע השינוי הזה?

האם השפה עצמה התפתחה הרבה בעשור האחרון? - לא. אמנם הגרסה הבאה שלה[א] אמורה להיות עליית מדרגה משמעותית - אך היא עדיין לא שם. אם כן ניתן לחפש את השינוי במקום אחר.

האם השפה רלוונטית במיוחד לבעיות חדשות בתעשייה, שלא היו נפוצות בעבר? - גם לא ממש.

השינוי במקומה של ג׳אווהסקריפט נבע משילוב של שלושה גורמים עיקריים: שיפור מנועי הג׳אווהסקריפט (תאימות לתקן וביצועים), התקדמות הווב כפלטפורמה פופולרית להפצת תוכנה וקהילה פעילה שבנתה ספריות משלימות שאפשרו להשתמש בג׳אווהסקריפט בצורה יעילה ואמינה יותר. בפוסט זה נתמקד בגורם השלישי.

שייך לסדרה: MVC בצד הלקוח, ובכלל.


סמל, שאם תכתבו ב Backbone.js - תיתקלו בו עוד הרבה...

רקע

לא ניתן להתעלם מתרומתה הרבה של ספריית jQuery ודומותיה[ב] בעיצוב מקומה החדש של שפת ג׳אווהסקריפט. במעגל השני של ההשפעה (לפחות בצד-הלקוח) נמצאות ספריות שעוזרות בכתיבת אפליקציות צד-לקוח גדולות. ספריות אלו נקראות ״MVC Frameworks" או ״ספריות ארכיטקטוניות״. השם MVC עשוי לבלבל את מי שכבר מכיר ועבד עם ספריות MVC צד-שרת כגון Struts או ASP.NET MVC.

בעוד ספריות "MVC קלאסיות" מטפלות באפליקציות רבות דפים והניווט בניהם, ספריות ה "JavaScript MVC" לרוב מטפלות באפליקציות דף-יחיד (כגון GMail) בהן יש מסך עקרי אחד שחלקים בו מתחלפים / משתנים - אך אין ניווט משמעותי. בכלל, עצם הפעולה בצד-הלקוח, ב Ajax, בו אין refresh לדף בעת פעולה - משנה את כללי המשחק: מציב בעיות חדשות ופתרונות אחרים. עוד הבדל בולט הוא שברוב ספריות ה "JavaScript MVC" אין Controller. השם MVC בא לבטא את הרזולוציה ואת הבעיה שהן באות לפתור: ארגון קוד באפליקציית UI גדולה ומורכבת.

קיימות כיום מספר רב של ספריות "JavaScript MVC" וצפוי בהן עוד תהליך של קונסולידציה. שלושת הגדולות הן:
  • Backbone.js כנראה הנפוצה והבוגרת מכולן. מספקת מסגרת כללית בלבד ומשאירה חופש / חללים למפתח להחליט ולמלא בעצמו.
  • Knockout.js ששמה במרכז את ה Data Binding ומאפשרת לכתוב בקלות אפליקציות "מונחות-נתונים" (מערכות מידע וכיוצ"ב) נפוצה במיוחד בעולם ה NET.
  • Ember.js (לשעבר Amber.js), ספרייה מקיפה שכוללת גם Class System[ג] ושואבת רעיונות רבים מ Rails ״כתוב מעט קוד, אך עשה זאת בדרך שלנו" צעירה יחסית - אך זוכה למומנטום משמעותי. נפוצה במיוחד בקרב מתכנתי רובי.
ספריות משמעותיות אחרות הן Spine.js, JavaScriptMVC, Batman.js ו AngularJS (מבית גוגל). האתר המצוין todoMVC מציע השוואה מעמיקה בין האפשרויות בכך שמימש אפליקציית דוגמה בכל אחת מספריות הנ"ל (ועוד כמה ספריות נוספות).


רגע של התפלספות

כפי שציינתי, ספריות "JavaScript MVC" הן לא בדיוק MVC קלאסי. בעצם, גם ל"MVC קלאסי״ יש כמה פרשנויות שונות (לדוגמה, האם ה Controller מתמקד בעיקר בטיפול ב input או אחריות על ניהול הflow והתלויות?[ד]).

המדקדקים מגדירים את Knockout.js כ MVVM) Model, View, View-Model) - תבנית עיצוב שמייקרוסופט מקדמת בעולם הNET. זה דיי נכון.
את Backbone.js ו Ember.js נוהגים להגדיר כ Model, View, Presenter) MVP). האמת, ספריות כמו ASP.NET או GWT מתאימות יותר להגדרה זו. הגדרה כגון Model, View-Controller, Router (בקיצור MVCR) תהיה יותר מדויקת.

ההגדרה המועדפת עלי כרגע היא !Model, View, Whatever (בקיצור MVW או *MV).
התחלתי את הפוסט הזה בניסיון לעקוב אחר ההיסטוריה של תבנית העיצוב MVC, הוריאציות והשינויים שהיא עברה במשך השנים, בעיקר MVP ו MVVM, והניסיון להסביר כיצד ספריות ה "JavaScript MVC" מתאימות לתבניות אלו.

בת'כלס - הן לא בדיוק מתאימות. הפוסט התארך, התפלפל והסתבך - וחששתי שלא יהיו מספיק קוראים (או אפילו בני-אדם דוברי עברית) שבאמת יתעניינו בכל ההגדרות הללו והניסיון להתאים אותן למציאות בשטח. הנחתי את הפוסט ההוא בצד.

בסופו של דבר - MVC הוא ניסיון לארגן אפליקציית UI מורכבת בצורה שתהיה קלה לתחזוקה ושינויים. כל דור של UI (Web, Fat Client, Fully Client Side, Command Line) - והאתגרים שלו.
כפי שמרטין פאולר ציין פעם: "אנשים נוטים להסתבך עם MVC. החלק הכי חשוב הוא לבצע הפרדה אמיתית בין Business Logic ל UI. כל השאר - משני". אני מסכים ב 100%.

לכל ספרייה יש את הדרך שלה לארגן את הקוד - ואולי יותר מעשי ומעניין פשוט להתבונן בישומים קונקרטיים.
בפוסט זה אתן הצצה בסיסית ביותר ל MVW בג׳אווהסקריפט בדמות Backbone.js - הספרייה הנפוצה והמינימליסטית שיכולה להיות בסיס לימודי מצוין להבנת MVC בג׳אווהסקריפט.


אז מה יש ב Backbone.js?

אקצר מעתה ואקרא ל Backbone.js פשוט BB.
  • BB היא קטנה למדי. 700 שורות קוד בערך. 1400 שורות עם הערות.
  • BB משתמשת ב underscore.js (ספרייה טובה ל javaScript Utils מאותו היוצר) ו jQuery. אם אתם עובדים עם BB - יהיה לכם מאוד נוח להשתמש גם כן בספריות אלו, וקצת פחות נוח (אך אפשרי לחלוטין) להשתמש בספריות אחרות. BB מעודדת שימוש בספריית Templating - ואתם יכולים לבחור את זו של underscore (תחביר JSP-like, שלי אישית, עושה צמרמורת) או כל אחת אחרת.
  • ל BB יש תיעוד טוב (reference) ו Tutorials סבירים. יש קהילה גדולה ופעילה.
  • בגלל ש BB פשוטה - מייחסים אותה כמתאימה למערכות פשוטות יחסית - אך בפועל יש גם מערכות גדולות מאוד ומורכבות שנכתבו בעזרתה.
  • מאוד קל לעבוד עם BB מול שרת שמספק REST APIs - במיוחד אם תבנית-הנתונים היא JSON.
  • ל BB יש ארכיטקטורת Plug-Ins וניתן למצוא Plug-Ins רבים שמרחיבים את יכולות-הבסיס שלה.
  • BB מספקת מסגרת לא-מחייבת. אם אתם רוצים לפעול קצת אחרת, יש לכם את החופש לעשות זאת. כאשר מדובר במבנה של האפליקציה שלי - אני אוהב את זה.
  • כמו ספריות "JS MVC" אחרות, BB מספקת:
    1. מערכת-מחלקות (Class System) עבור Model ו Views (וגם Routers ו Collections - שהם רכיבים מרכזיים אחרים בBB)
    2. ניהול של המודלים (ה instances), כולל הקשרים בניהם.
    3. כלים לאימות ערכים (Validation Logic) במודל.

הנה דוגמה למודל מינימלי שניתן לייצר ב BB:
הערה: משפטי ה expect...toBe הן בדיקות-יחידה המציגות את התנהגות הקוד. כל הבדיקות עוברות בהצלחה.

המודל שלנו, Person בדוגמה זו, הוא פשוט למדי. הייתי יכול ליצור אותו גם ללא defaults - רק עלי ידי הורשה ריקה מהמחלקה Backbone.Model. תכונות המודל (כמו שם פרטי/משפחה) הן דינמיות ומוגדרות בזמן יצירת האובייקט הספציפי. ה default היא היכולת לחייב שתכונות מסוימות יהיו בכל מקרה.

ב1 - אנו יוצרים מודל של אדם בשם אוזי כהן.
ב2 - בכדי להדגיש את השוני בינו ובין אמן אחר בשם ״ליאונרד״ ובעל שם משפחה זהה, אוזי משנה את שם המשפחה שלו.
ב3 - אנו מקבלים את המודל המלא בפורמט JSON.

כמובן שדוגמה זו היא טיפה בים. מודלים אמיתיים הם מורכבים בהרבה ו BB מספק עוד יכולות רבות. המטרה היא רק לתת הצצה מוחשית כיצד נראה שימוש ב BB. הנה עוד דוגמה, שכוללת גם View:

השתמשתי במודל הכי פשוט שאפשר - כי ברצוני להתמקד ב View.

PersonView היא המחלקה שאחראית לצייר מודל של Person. יכולים להיות, כמובן, כמה Views שונים לאותו המודל. את ה markup שה View יוצר, ארצה לשלב היכנשהו ב DOM. בדוגמה זו - זה יהיה container$, שמשתמש ב span בכדי לא לבלבל עם ה div ש Backbone יוצר באופן אוטומטי לכל מופע של המודל.

el הוא בעצם אלמנט ה DOM שיצר לי BB כחלק מה View. הוא עושה זאת על מנת לאפשר ל markup בפנים להשתנות, בלי שאצטרך לחבר את ה markup שוב ל container. הבחירה ב div היא כמובן רק default, ואם אני זקוק ל list element (תג <li>) - אפשר לקבוע זאת.

ב1- אני בודק מה יש בתוך ה container - ניתן לראות את ה markup שנוצר בתוך הפונקציה render. זו דוגמה דיי מכוערת - לרוב נשתמש בספריית templating על מנת שהקוד שיוצר את ה html snippet יהיה אלגנטי. לא רציתי להעמיס על דוגמה זו ולכן הקוד ה"פרימיטיבי".

ב2- אנחנו בוחנים את ה markup כולל ה container. רק לוודא שהתמונה הכללית ברורה.

ב3- אנחנו מבצעים שינוי למודל ורואים שה markup ב container השתנה באופן פלאי. "פלאי" - כמובן שלא. אנחנו כותבים את הקוד שעושה זאת - backbone רק מכווין אותנו למבנה מסוים ועוזר לנו.

בואו נבחן את הפונקציה initialize שהיא לצורך העניין הבנאי (constructor) של מחלקת ה PersonView שלנו, וכוללת 2 משפטים מוזרים:
ב5- אנחנו מבצעים פעולת JavaScripts's bind לכל המתודות במחלקה שאנו הולכים ל"ייצא" כ callbacks לאובייקטים אחרים. במקרה זה - יש רק את ״render״, אך יכולתי באותה מידה לשרשר גם שמות של מתודות נוספות. למה צריך לעשות JavaScript bind? - כדי ש this ימשיך להצביע לאובייקט שלנו. מבולבלים? קראו את הפוסט להבין את JavaScript this.
הסימן "_" (קו תחתון) הוא הקיצור לגשת לספרייה underscore.js, ממש כמו שניגשים עם $ ל jQuery. אלו המקומות ש underscore מסייעת מאוד לכתוב קוד ב BB. אתם לא חייבים - אבל כדאי.

ב6- אנחנו "מחייטים" את ה View שלנו לבצע פעולת render כל פעם שהמודל משתנה (change' event'). מאוד דומה לאירועים ב jQuery.
הבהרה קלה: אם אתם מכירים את תבנית העיצוב MVC אתם יכולים להבחין שה View ב Backbone מבצע פעולות ("חיוט") של Controller. זה נכון: אפשר לומר שה View ב BB הוא בעצם View-Controller: הוא עושה הרבה פעולות שבמקור יוחסו ל Controller.
אם אתם מכירים את תבנית העיצוב MVP נראה שה View הוא בעצם יותר Presenter. הוא אחראי ל Presentation Logic. זה נכון: בעצם מה שנקרא "View" ב BB הוא דומה ל MVP's Presenter בעוד ספריית ה Templating (בעצם ה Template עצמו) - דומה מאוד ל MVP's View.


סיכום

הצצנו ל Backbone וטעמנו כיצד נראה קוד שמשתמש ב Backbone.js. טעימה קטנה.
דיברנו גם על החשיבות של ספריות "JavaScript MVC" - כאשר קוד הג'אווסקריפט שלכם מתחיל לגדול ולהסתבך. וגם על החשיבות לא לחפור בתבנית-העיצוב MVC וההיסטוריה שלה יותר מדי :)

ייתכן ואמשיך לכתוב קצת על Backbone על מנת להתעמק בו קצת יותר, בניסיון לבחון JavaScript MVC "מהשטח".

בהצלחה!


------

[א] נקראת Harmony, ג׳אווהסקריפט 2.0 או ECMAScript 6.0. כוללות, ככל הנראה, מחלקות, מודולים (חבילות של מחלקות עם ניהול תלויות), תיקונים לכמה כשלים של שפת ג׳אווהסקריפט ועוד. ממש מהפיכה!

[ב] Prototype ו MooTools היו גם הן מועמדות ראויות, אך קהל המפתחים בחר לבסוף בjQuery. ניתן לקרוא על העיקרון מאחורי jQuery בפוסט מבוא מואץ ל jQuery.

[ג] כלים / Framework להגדרת מחלקות וכל מה שקשור בהן. בג'אווהסקריפט, כפי מתואר בפוסט מחלקות בג'אווהסקריפט. בשפת ג'אווהסקריפט לא קיימות מחלקות - ועל המתכנת לייצר אותן לבד. בעוד ספריות "JavaScript MVC" רבות מספקות סביבה אחידה / מוגנת ליצירת מחלקות עבור האלמנטים הבסיסיים בספריה (כמו Model או View).- ספריית Ember מספקת כלים שתוכלו להשתמש בהם עבור כל המחלקות בפרויקט שלכם.

[ד] המקור של MVC הוא בשפת Smalltalk בשנות ה-70. כשהגיעו ה Fat Clients (ב ++C), ולאחר מכן הווב - נוצרו פרשנויות ווריאציות שונות ל MVC על מנת להתמודד עם האתגרים החדשים שהציבו טכנולוגיות אלו.



תגובה 1:

  1. תודה רבה על הפוסט המעניין. התחלתי להכנס ל Backbone לא מזמן. בעוד שהרבה לא יאהבו את העובדה שצריך לחבר את כל החלקים בעצמך (כלומר ללא Magic), אני מאוד אוהב את זה, אולי כי אני אוהב תמיד לשלוט בקוד שלי ב-100%.

    השבמחק