שדרגו את האתר שלכם עם פונטים בעברית

גם אם אתם לא נעזרים במעצב כדי לבנות את האתר או אפליקציית ה-web שלכם, זה לא אומר שהם לא צריכים להיראות במיטבם. ואכן, מפתחים רבים משקיעים זמן רב בעיצוב האתר שבנו בעצמם, אבל שוכחים פרט אחד קטן שיכול לגרום לאתר להיראות מהוקצע יותר – אני מדבר כמובן על שימוש בגופן (פונט, font) שאינו אחד מברירות המחדל של הדפדפן.

אני כבר יכול לשמוע את רווחת האנחה של חלקכם: "אצלי הכל בסדר, אני משתמש בפונט Arial…". אז זהו – שזה לא פתרון מספיק טוב. גופן אריאל (וכמוהו כל הפונטים שמגיעים עם הדפדפן / מערכת ההפעלה), למרות היותו קריא ונוח, הפך כבר מזמן לשחוק ומאוס בעיני משתמשים רבים. השימוש בו עלול לגרור תגובות כמו "האתר הזה נמצא עדיין בבנייה" או "האפליקציה נראית לא מקצועית".

כדי להמחיש כמה משמעותי ההבדל, הנה כמה תמונות מסך מאתר allmovies.co.il, שההבדל היחיד ביניהן הוא הפונט:

שימוש בפונט ברירת המחדל של הדפדפן
שימוש בפונט ברירת המחדל של הדפדפן (Chrome)
שימוש בפונט הנפוץ אריאל
שימוש בפונט הנפוץ "אריאל"
שימוש בפונט החינמי אלף
שימוש בפונט החינמי "אלף"

שימו לב איך השימוש בפונט "אלף" (בתמונה השלישית) מפיח באתר אופי ייחודי, וגורם לו להיראות מקצועי יותר מהשניים האחרים.

מה חשוב כשבוחרים פונט?

לא כל גופן הינו איכותי ומתאים לשימוש בדפדפן. כאשר אנו בוחרים פונט, נשתדל לבחור אחד בעל כמה שיותר מהתכונות הבאות:

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

    הנה קוד המאפשר לייצר את התמונה למעלה.

  2. מגיע במספר פורמטים. קיימים מספר סוגים נפוצים לקבצי פונט, כגון TTF, WOFF, SVG ועוד. חלקם נראים טוב יותר בדפדפנים מסויימים, חלקם שוקלים פחות, וחלקם נתמכים רק בדפדפנים חדשים. השתדלו לבחור גופן המגיע בכמה שיותר פורמטים, כך שייראה במיטבו על כל הדפדפנים הנפוצים.
  3. מאוחסן ב-CDN. שימוש בפונט המאוחסן ב-CDN (רשת דיוור תוכן), עשויה לחסוך את הורדת הפונט ע"י הדפדפן במידה והורד כבר בעבר. בנוסף, שימוש ב-CDN מאפשר הורדת הפונט במקביל לתוכן אחר באתר שלכם, ובכך האצת טעינת האתר וצמצום תופעת ה-FOUT.
  4. בעל איכות גבוהה וכמה שפחות פגמים. ניתן לוודא זאת ע"י הגדלת הפונט ומעבר על האותיות אחת אחת.

איפה מוצאים פונטים בעברית?

אם האתר שלכם כתוב באנגלית, יש לכם מזל. קיימים מאות פונטים איכותיים וחינמיים באנגלית. רבים מהם ניתן למצוא באתר Google Fonts.

אך אם אתם כמוני – ואוהבים לבנות אתרים בעברית, אל דאגה! אספתי מספר פונטים חינמיים המתאימים לשימוש ב-web.

אלף

פונט מוכר זה הוא אחד הפונטים העבריים היחידים הנתמכים ע"י Google Fonts. אלף תומך בעברית ואנגלית, ומגיע במשקלים רגיל ומודגש בלבד.

Open Sans בעברית

גופן Open Sans הינו נפוץ מאוד בשפות רבות. לאחרונה עוצבה הגרסה העברית שלו ע"י ינק יונטף, והיא מסופקת ע"י Google Fonts להורדה בחינם, במשקלים רבים.

אבל… הגופן המסופק ע"י גוגל לא מוצג בדפדפן אינטרנט אקספלורר! הפתרון המועדף עליי הוא להשתמש בגרסה הבאה שנוצרה מחדש ע"י אסף כץ.

Noto בעברית

פונט זה הוא הנסיון של גוגל לפתח פונט בעברית התומך בכל השפות. הוא מגיע במשקלים רגיל ומודגש. להורדת גופן Noto בעברית.

גופנים נוספים

קיימים גופנים רבים בעברית להורדה בחינם, אך יש להבחין בין גופנים רגילים, לכאלו המתאימים לשימוש ב-web וייראו טוב בכל הדפדפנים. האתר פרויקט הפונטים מרכז גופנים חינמיים בעברית המתאימים לשימוש ב-web.

Redux – ארכיטקטורת Flux, אבל כמו שצריך

אם עבדתם אפילו קצת עם הספריה הנהדרת React מבית פייסבוק, כנראה שהבנתם (או לפחות ניסו להסביר לכם) את ההבדל בין מאפייני רכיב (props) למצב שלו (state). ואכן בפוסט המצויין Thinking in React אנו מוצאים הסבר מפורט על איך להחליט איזה מידע שייך לכל אחד מהם (בגדול, כל מה שיכול להגיע מבחוץ יהיה בפרופס, וכל מה שישתנה ע"י הרכיב וגם באחריותו יהיה ב-state).

ארכיטקטורת Flux, שהתפרסמה גם היא ע"י פייסבוק ביחד עם React, לוקחת את הקונספט של MVC צעד אחד קדימה, ע"י הגדרת מקומות המרכזים state גלובאלי, אלו הם ה-Stores. כדי לשמור על זרימת נתונים ברורה וצפויה, שינויים למידע ב-stores לא מתבצעים ישירות, אלא ע"י יצירת אירועים הנקראים פעולות (Actions), עליהם מאזינים ה-stores ומבצעים שינויים במידע בהתאם. ה-Actions נוצרים לרוב בעקבות פעולת משתמש, לדוגמה לחיצה על כפתור. על כל אלו מנצח ה-Dispatcher שדואג שהפעולות ישוגרו בסדר הגיוני ולא בו זמנית. רכיבי ה-React עצמם מאזינים לשינויים ב-stores, ומעדכנים את ה-state של עצמם בהתאם.

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

חשוב לציין ש-Flux עצמה היא רק design pattern, ולא תשתית קוד. למעשה הקוד היחיד המסופק ע"י פייסבוק הוא ה-Dispatcher, כל השאר הן רק מוסכמות. ולמרות ש-Flux חדשנית ומשתלבת יפה עם React, היא עדיין מרגישה מעט בוסרית ולא חלקה כמו שהיינו מצפים. לדוגמה, בכל רכיב (Component) שצריך גישה למידע ב-stores, אנחנו צריכים להירשם לקבלת עדכונים, ולעדכן את ה-state בכל שינוי:

function getTodoState() {
    return { allTodos: TodoStore.getAll() };
}

var TodoApp = React.createClass({
    getInitialState: function() {
        return getTodoState();
    },
    componentDidMount: function() {
        TodoStore.addChangeListener(this._onChange);
    },
    componentWillUnmount: function() {
        TodoStore.removeChangeListener(this._onChange);
    },
    _onChange: function() {
        this.setState(getTodoState());
    }
};

כמה boilerplate! וזה עוד לפני שהתחלנו לממש את render… הדוגמה הזאת (שהיא אגב, דוגמה רשמית של Flux) גם מעלה שאלות שהתשובות עליהן לא פשוטות בכלל:

  1. מה עם רכיבים ילדים? האם לחלחל אליהם את ה-state ידנית או שגם הם יאזינו ל-store?
  2. מה קורה אם השינוי ב-store לא היה רלוונטי לרכיב? האם להתעלם ולרנדר? אולי לבדוק את השינוי? אולי PureRenderMixin?
  3. מה קורה אם יש שני stores? צריך להירשם לשניהם? או אולי לאחד אותם? ומה אם הם תלויים אחד בשני?
  4. איך בוחרים איזה מידע ישב ב-store ואיזה ב-state של רכיבי ה-React עצמם?

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

Redux להצלה

אז למרות ש-Flux הוא לא הדבר הכי נוח בעולם, העיקרון של state יחיד גלובאלי המשיך להתפתח בקהילת הקוד הפתוח, ובמהרה החלו לצוץ חלופות רבות כגון: ReFlux, Fluxible, Flummox, Fluxette (נשבע לכם שאני לא ממציא) ו-Alt. אך נראה שהמנצחת הגדולה בהתקוטטות הזאת היא ספריית Redux מאת Dan Abramov (נשמע ישראלי, אבל הוא לא).

הספרייה הזאת (וגם כמה אחרות) לוקחת כמה צעדים קדימה את העיקרון של Flux:

  1. ה-store הוא יחיד, והוא מפוצל לשני חלקים: אובייקט state יחיד לכל האפליקציה, ופונקצייה טהורה אחת בשם Reduce המקבלת את אובייקט ה-state הנוכחי ו-action כלשהו, ומחזירה state חדש.
  2. ה-state היחיד הוא היררכי, וניתן להרכיב (לשלב) Reducers שונים כך שכל אחד יטפל בחלק אחר של ה-state. כך ניתן לפתח בנפרד חלקים שונים של האפליקציה.
  3. רכיבי React לא צריכים להכיל state בכלל. כל המידע שלהם צריך להיות מסופק בתור props מרכיב האבא ו/או מה-state הראשי.

שימו לב שהסעיף הראשון פותר בנקל את בעיית הרינדור בשרת, מכיוון שכעת ה-state הוא יחיד, גלובאלי, ואפשר לטעון אותו בתחילת כל בקשת HTTP ולשמור אותו חזרה בסופה, למשל בתור JSON.

בנוסף לשינוי בארכיטקטורה, הספריה מספקת מספר פונקציות בסיסיות, שביחד עם כמה מוסכמות, הופכות כל רכיב React לישות המורכבת (בגדול) מ-3 חלקים:

  1. פונקציית render, כמו שאנחנו כבר מכירים, שהיא לרוב פונקצייה טהורה, כלומר דטרמיניסטית ותלוייה אך ורק בקלט שלה ולא בשום מידע חיצוני.
  2. פונקציית mapStateToProps, שכשמה כן היא, מקבלת את ה-state הגלובאלי (ולעתים גם props מרכיב האבא) ומייצרת את ה-props הסופיים של הרכיב.
  3. אובייקט או פונקציה בשם mapDispatchToProps הממפה בין props של הרכיב ל-Action Creators, שהן פונקציות המחזירות אובייקט action אותו יש להעביר ל-reducer ובכך לשנות את ה-state.

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

ולמה זה טוב יותר?

אז מעבר ליתרונות שציינתי קודם (פחות קוד, קל לרנדר בשרת), Redux מקלה משמעותית על ביצוע Hot Swapping של רכיבי React בזמן ריצה. כלומר לאחר ששינינו את קוד הרכיב, ניתן להחליף רק אותו, ללא טעינה מחדש של כל הדף! אפשרות זאת יכולה לחסוך הרבה זמן פיתוח, במיוחד אם אנחנו נדרשים לבצע הרבה פעולות ידניות עד שמגיעים למצב שאפשר לבדוק את הרכיב.

יתרון נוסף של Redux הוא נוחות כתיבת בדיקות. מכיוון שה-Reducers הן פונקציות טהורות, הן צפויות לחלוטין ולכן קל מאוד לכתוב להן בדיקות יחידה המקבלות state ו-action, ומוודאות שה-state החדש הוא מה שציפינו. באופן זה אנו מפרידים לחלוטין את כל בדיקות ה-business logic מבדיקות ה-UI.

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

אוקיי, אני רוצה Redux, איך מתחילים?

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

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

רוצים ללמוד עוד? בבקשה:

לקריאה נוספת

פרוטוקול HTTP – מדריך למתחילים

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

פרוטוקול HTTP – מה זה ולמה צריך את זה?

Hypertext transfer protocol, ובקיצור HTTP, הינו פרוטוקול שרת-לקוח מבוסס טקסט. במילים פשוטות, HTTP מגדיר שפת טקסט פשוטה המאפשרת לצרכן שירות כלשהו – הלקוח – לבקש משאבים מנותן שירות כלשהו – השרת.

לקוח ה-HTTP הנפוץ ביותר הוא הדפדפן, המבצע בקשות אל מול שרת אינטרנט להורדת דפי אינטרנט, תמונות וכדומה. אך זהו לא השימוש היחיד בפרוטוקול: HTTP משמש גם תוכנות אחרות מבוססות שרת לקוח כגון אפליקציות מובייל, מערכות החושפות REST API, שרתי source control, תקשורת בין רכיבים במערכות מורכבות ועוד אינספור דוגמאות אחרות.

כפי שהזכרתי קודם, פרוטוקול HTTP הינו מבוסס טקסט, כלומר כל המידע העובר בפרוטוקול הינו טקסט קריא בלבד, ואין בו מידע "בינארי". תכונה זאת של HTTP, גם אם מוסיפה מעט לנפח המידע הנשלח, מקלה מאוד על תהליכי הפיתוח, הניתוח ואיתור השגיאות, פשוט כי תמיד ניתן להסתכל בעיניים על המידע ולהבין מה הולך.

במודל 4 השכבות (וגם בזה המכיל 7), HTTP שייך לשכבה הגבוהה ביותר – שכבת היישום (האפליקציה). כלומר כדי להשתמש בפרוטוקול HTTP אנחנו צריכים לדאוג קודם כל לחיבור אמין בין השרת ללקוח, שיאפשר לנו העברת טקסט בין שתי הישויות. אחרי שייצרנו חיבור כזה, שכמעט תמיד יהיה מסוג TCP, פרוטוקול HTTP למעשה מגדיר איך יראה המידע שיעבור בין השרת ללקוח, כלומר איך תיראה בקשה למידע, ואיך תיראה תגובה אליה.

מבנה בקשת HTTP

השלב הראשון בשיחת HTTP הוא שליחת בקשה מהלקוח לשרת. הבקשה נשלחת ע"ג חיבור TCP שנפתח בין הלקוח לשרת, ובפרט לפורט עליו מאזין השרת. באינטרנט יהיה זה בד"כ פורט 80, אך ניתן להשתמש בכל פורט.

פרוטוקול HTTP - בקשה
שליחת בקשת HTTP מהלקוח לשרת

כל בקשת HTTP היא למעשה טקסט רגיל המורכב משלושה חלקים המופרדים ביניהם בשורת רווח (התווים \r\n):

  • שורת הפתיחה
  • שורות כותרת (headers)
  • גוף הבקשה (לא חובה)

שורת הפתיחה

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

  • שיטת הבקשה (method) היא מילה אחת המתארת מה מבקש הלקוח מהשרת לעשות. בפרוטוקול HTTP 1.1 קיימות 8 שיטות, שהנפוצות ביניהן הן GET המבקשת לקבל משהו מהשרת, ו-POST השולחת מידע לשרת. מבחינת הפרוטוקול עצמו, ההבדל בין השיטות הוא כמעט סמנטי בלבד (אין כמעט הבדל בין GET ל-POST), אך בפועל השרתים עצמם הם אלו שיוצרים את ההבדל כאשר הם מתייחסים בצורה שונה לגמרי לכל שיטה (GET מחזיר מידע, POST מעלה מידע).
  • כתובת המשאב המבוקש המציינת לשרת לאיזה אובייקט נרצה לגשת. הכתובת יכולה להיות אבסולוטית, לדוגמה http://icode.co.il/file.html או יחסית: /file1
  • גרסת הפרוטוקול לפיה נבנתה הבקשה, לרוב HTTP 1.1

הנה דוגמה לשורת פתיחה מלאה:

GET /index.html HTTP/1.1

כדי להמחיש כמה פשוט לקרוא פרוטוקול HTTP בקוד, להלן דוגמה לשורת פייתון המחלצת את שורת הפתיחה בבקשת HTTP, ומפרקת אותה (בהנחה שמבנה הבקשה תקין) למרכיביה:

method, url, version = HTTP_REQUEST.split('\r\n')[0].strip().split(' ')

שורות הכותרת

החלק השני בכל בקשת HTTP הוא אוסף של שורות כותרת (header lines) המכילות מידע נוסף על הבקשה (metadata) הדרוש לשרת כדי להשלים אותה. כל כותרת מורכבת משם וערך המופרדים בנקודותיים ורווח. לדוגמה:

Accept-Language: he

שורת כותרת זאת, ששמה Accept-Language וערכה he (קיצור של Hebrew) מציינת לשרת שהתשובה לבקשה יכולה להכיל מידע בעברית.

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

החל מגרסה 1.1 קיימת כותרת, Host, אותה חובה להעביר בכל בקשה. כותרת זאת היא כתובת האתר אליו מפנים את הבקשה, והיא נדרשת כדי שהשרת יוכל לתמוך בכמה אתרים במקביל (דוגמה בהמשך).

גוף הבקשה

חלק מהבקשות יכולות להכיל מידע אותו מבקש הלקוח להעביר לשרת. מידע זה נקרא גוף הבקשה והוא החלק היחיד בבקשת HTTP שלא חייב להיות טקסטואלי (כלומר הוא יכול להיות גם מידע בינארי כמו קובץ תמונה).

לדוגמה בבקשת POST גוף הבקשה עשוי להכיל את המידע אותו נרצה לשמור בשרת (פרטי טופס, קובץ וכד'). בקשות GET לרוב לא מכילות גוף.

כאשר מעבירים את גוף הבקשה, יש להוסיף את הכותרת Content-Length המציינת את אורך גוף הבקשה, בבתים (דוגמה בהמשך).

דוגמאות לבקשת HTTP

להלן דוגמה לבקשת GET המכילה שתי כותרות:

GET /index.html HTTP/1.1
Host: icode.co.il
Accept-Encoding: gzip, deflate

בדוגמה זאת אנו מבקשים מהשרת להחזיר את העמוד index.html מהאתר icode.co.il, ומוסיפים שאנו תומכים בקבלת מידע דחוס מסוג gzip.

והנה דוגמה לבקשת POST המכילה גוף. שימו לב לשורת הרווח בין הכותרות לגוף הבקשה:

POST /submit.html HTTP/1.1
Host: icode.co.il
Content-Length: 10

name=tzach

בדוגמה האחרונה, הכותרת Content-Length מכילה את האורך של גוף הבקשה בבתים, במקרה שלנו זהו האורך של הטקסט name=tzach, שהוא בדיוק 10 בתים.

בניית בקשת HTTP בקוד

בדומה לקריאת בקשת HTTP, גם הבנייה שלה היא קלה מאוד לקידוד. הנה דוגמה לבניית בקשה, בהינתן כל החלקים שלה:

request = "{method} {url} HTTP/1.1\r\n{headers}\r\n\r\n{body}".format(
    method = METHOD,
    url = URL,
    headers = '\r\n'.join('%s: %s' % item for item in HEADERS_DICT),
    body = BODY)

מבנה תגובת HTTP

השלב השני והאחרון בכל שיחת HTTP הוא התגובה (response) אותה שולח השרת ללקוח עבור הבקשה. התגובה, כשמה כן היא, מכילה את התשובה של השרת לבקשה. למשל אם הלקוח ביקש קובץ מסויים מהשרת, התגובה עשויה להכיל את תוכן הקובץ, או הודעת שגיאה (למשל במקרה שהקובץ לא קיים).

פרוטוקול HTTP - תגובה
לאחר קבלת הבקשה, השרת שולח תגובה בחזרה ללקוח

בדומה לבקשה, גם התגובה מורכבת משלושת החלקים:

  • שורת מצב המכילה את גרסת הפרוטוקול בה השתמש השרת וקוד מצב.
  • שורות כותרת באותו מבנה כמו בבקשה (ולאחריהן שורת רווח)
  • גוף התגובה (לא חובה)

קוד המצב

נסתכל על דוגמה לשורת מצב החוזרת מהשרת:

HTTP/1.1 200 OK

בדוגמה זאת הערך 200 (בתוספת המחרוזת OK) נקרא קוד המצב (status code) והוא מציין את התוצאה של נסיון השרת לטפל בבקשה שהתקבלה, לדוגמה: הצלחה (200), משאב לא נמצא (404) או שגיאה כללית (500).

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

  • 1XX – בקשה התקבלה אך אין עדיין תגובה (מאוד נדיר וכמעט לא בשימוש).
  • 2XX – הצלחה. הערך הנפוץ ביותר בקבוצה זאת הוא 200 OK, המציין תגובה תקינה לבקשה, אך קיימים גם ערכים אחרים כגון 201 (נוצר).
  • 3XX – המשאב נמצא אבל הועבר. לרוב התגובה תהיה 301 (הועבר באופן קבוע) או 302 (הועבר זמנית).
  • 4XX – קיימת שגיאה בבקשה עצמה, למשל התבקש משאב לא קיים (404), אין הרשאות (403), יש צורך לבצע אימות (401) ועוד.
  • 5XX – התרחשה שגיאה בשרת. שני הערכים הנפוצים הם 500 (שגיאה כללית) ו-503 (שגיאה זמנית).

שורות הכותרת

בדומה לבקשת HTTP, גם התגובה מכילה שורות כותרת (header lines), אשר כל אחת מהן מורכבת משם וערך. לרוב, כל כותרת תהיה אחת מהכותרות הסטנדרטיות בפרוטוקול HTTP, אך למעשה ניתן להחזיר כל כותרת שבא לנו, או אפילו לא להחזיר כותרות כלל.

חלק גדול מכותרות התגובה מתחלקות לקבוצות הבאות:

  • כותרות המתארות את תוכן התגובה: סוג המידע שחזר, אורכו, תאריכי היצירה והשינוי, מזהה ייחודי, עוגיות שהשרת מעוניין לשמור בצד הלקוח וכד'.
  • כותרות המתארות את השרת: סוג השרת, תכונות הנתמכות ע"י השרת, וכד'.
  • כותרות המשפיעות על החיבור בין השרת ללקוח: מידע על אבטחה, או למשל, השרת יכול להודיע ללקוח שהוא סגר את חיבור ה-TCP.

להלן דוגמה לתגובה:

HTTP/1.1 200 OK
Date: Fri, 8 Jan 2016 11:29:34 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 31

<html><body>Hello</body></html>

תגובה זו מכילה שלוש שורות כותרת:

  • Date – שערכה הוא הזמן בשרת בעת יצירת התגובה.
  • Content-Type – שמציינת את סוג המידע אותו מכיל גוף התגובה, במקרה שלנו מדובר בקוד HTML בקידוד UTF-8.
  • Content-Length – אורך גוף התגובה בבתים.

גוף התגובה

לעתים קרובות השרת ירצה להחזיר ללקוח מידע כלשהו בתגובה לבקשה. מידע זה נקרא גוף התגובה, ויכול להיות מכל סוג. דוגמאות נפוצות לגוף התגובה הן קוד HTML, CSS או Javascript, מידע כלשהו בקידוד JSON או XML, קובץ בינארי כגון תמונה, gzip או מסמך.

במידה והתגובה מכילה גוף, היא חייבת להכיל גם את הכותרת Content-Length, שתציין את אורך הגוף בבתים. במידה והכותרת לא קיימת, הלקוח עלול להחשיב את התגובה כלא חוקית ולהתעלם ממנה.

נבחן דוגמה נוספת לתגובה:

HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Content-Length: 23

{"error": "unexpected"}

בדוגמה זאת השרת החזיר קוד מצב 500 המציין שגיאה בשרת. בנוסף, גוף התגובה מטיפוס JSON מכיל מידע נוסף על השגיאה. שימו לב לכותרת Content-Type המכילה את סוג המידע החוזר (הנקרא לפעמים גם MIME type או Media type).

אז מה עושים עם כל המידע הזה?

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

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

מתוך דפדפן כרום, לחצו F12 לפתיחת תפריט המפתחים, ולאחר מכן לחצו על לשונית Network וטענו מחדש את הדף:

Chrome Dev Tools

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

מה הלאה?

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

שימוש בפונט להצגת אייקונים באתר שלכם

בשנים האחרונות הפך הטרנד של "עיצוב שטוח" (flat design) לנפוץ במיוחד. אחד המאפיינים הבולטים של גישה עיצובית זו, בעיקר באפליקציות web, הוא השימוש באייקונים מונוכרומטיים (בעלי צבע אחד). דוגמה שכרגע נמצאת לי מול העיניים היא מערכת הניהול של wordpress:

Flat icons
אייקונים מונוכרומטיים

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

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

SVG – תמונה וקטורית

Smart Vector Graphics, או SVG הוא פורמט תמונה וקטורי (vector) מבוסס XML. לא ארחיב עליו כאן, אך אקצר ואסביר שבשונה מפורמטים כמו Bitmap או JPEG, קובץ SVG לא מכיל מידע אודות הפיקסלים שבתמונה, אלא מתאר את האלמנטים השונים המרכיבים את התמונה כגון קווים, צורות, טקסט וכד'. הנה דוגמה לקובץ SVG.

לשימוש ב-SVG ישנם מספר יתרונות על שימוש בפורמטים שאינם וקטורים:

  • ניתן לשנות את גודל התמונה ללא פגיעה באיכות
  • קובץ התמונה הוא לרוב קטן יותר, במיוחד עבור תמונות פשוטות
  • ניתן לשנות את העיצוב באמצעות CSS
  • הקובץ הוא בפורמט טקסטואלי וניתן לייצר אותו דינאמית ואף להטמיע אותו ישירות ב-HTML

ניתן להשתמש ב-SVG יחסית בחופשיות, מכיוון שפורמט זה נתמך על ידי הרוב המוחלט של הדפדפנים הנפוצים.

אך למרות גודל הקובץ הקטן, טעינת קובץ SVG עדיין מצריכה בקשת HTTP. במידה והעמוד שלנו מכיל הרבה אייקונים, טעינתם עלולה לפגוע בביצועי האפליקציה שלנו. אמנם ניתן לייצר sprite אחד מכל הקבצים, אך התחזוקה והשימוש בקובץ כזה היא לא נוחה בלשון המעטה.

למזלנו, קיים פתרון שימושי ביותר לבעייה זו:

פונט אייקונים

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

קם חכם ואמר – מדוע צריכים הפונטים להכיל אותיות בלבד? למה שלא נייצר פונט המכיל, במקום אותיות, אייקונים? ואכן קיימים מספר פונטים שהם למעשה אוסף של אייקונים, ביניהם Font Awesome ו-Ionicons.

לפונט אייקונים יתרונות רבים:

  • בקשת HTTP אחת, ללא תלות במספר האייקונים אותם אנו צריכים.
  • תמיכה לאחור גם בדפדפנים ישנים מאוד (IE6).
  • ניתן לשנות את עיצוב האייקונים בנקל ע"י מאפייני CSS לעיצוב טקסט: font-size, color וכד'
  • השימוש בפונטים אלו הוא פשוט מאוד, ומצריך 2 שורות קוד בלבד:
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
<i class="fa fa-camera-retro"></i>

השורה הראשונה מייבאת את קובץ ה-CSS של Font Awesome (שבתורו מייבא את הפונט עצמו), והשניה מייצרת תגית ריקה, שהחלק החשוב בה הוא המאפיין class, ה"ממלא" את התגית בתוכן המתאים מתוך הפונט (גם כאן לא נגלוש לפרטים).

וכך זה נראה:

JS Bin on jsbin.com

עבור אתרים ואפליקציות פשוטים – השימוש ב-Font Awesome ודומיו מספק בהחלט, בעיקר כי פונטים אלו מכילים אייקונים עבור רוב הפעולות הנפוצות. אך מה אם יש לנו אייקונים משלנו שנרצה לשלב בפונט? בדיוק בשביל משימה זאת קיים השירות המדהים הבא.

Fontello

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

  • ע"י בחירה ממבחר קיים של מאות אייקונים מפונטים נפוצים כגון Font Awesome, Fontelico, Entypo ועוד.
  • ע"י העלאה של קבצי SVG משלנו.

לאחר בחירת האייקונים, ניתן להוריד את הפונט, שמגיע ב-4 פורמטים שונים ונתמך בכל הדפדפנים, ואיתו קבצי CSS המאפשרים שימוש באייקונים בקלות:

<link rel="stylesheet" href="path/to/fontello.css">
<i class="icon-like"></i>

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

ואם בא לכם לייצר אייקון חדש מאפס או מקובץ וקטורי שאינו SVG, אני ממליץ על תוכנת Inkscape החינמית.

האתר שלי למטה?!?

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

  1. בעיה בשרת – האתר באמת למטה
  2. בעיה מקומית במחשב / ראוטר / ספק האינטרנט שלי, והאתר בעצם למעלה

האתר הגאוני Down For Everyone or Just Me עוזר לנו להבדיל במהירות בין שני המקרים ע"י ביצוע בדיקה פשוטה של בדיקת האתר שלנו. מכיוון שהבדיקה לא מתבצעת מהמחשב שלנו, היא כנראה משקפת את המצב האמיתי של האתר.

כך זה נראה כשבדקתי אם google.com למעלה:

downforeveryoneorjustme

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

איך בוחרים web framework בפייתון

אם יצא לכם לעבוד על פרוייקט מבוסס web בפייתון, בין אם אתר פשוט או מערכת SAAS מורכבת, סביר להניח שהשתמשתם בתשתית כלשהי, אחרת הייתם צריכים לכתוב הרבה מאוד קוד רק בשביל להתחיל.

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

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

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

הכל שאלה של עלות ותועלת

ניתן לסכם את כל הנושא של בחירת תשתית בהשוואה בין עלות ותועלת.

התועלת היא אוסף הדברים שהתשתית מספקת לנו או מקלה עלינו לממש. למשל, האם היא מספקת שירותי ORM? ניהול sessions? אולי cache? וכד'. כמובן שהעלות מתבססת על דרישות הפרוייקט שלנו. אם אנחנו לא צריכם גישה לבסיס נתונים, אין טעם להעדיף ספרייה המכילה ORM. זה לא אומר שנעדיף אותה פחות, אלא רק שלא נתחשב ביתרון הזה שלה.

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

  • עקומת הלמידה של החבילה החדשה; האם התנסו בה בעבר? האם התיעוד שלה מקיף ואיכותי?
  • הקושי של התחלת פרוייקט חדש.
  • כמה קוד נצטרך לכתוב כדי לממש את הפרוייקט שלנו? או במילים אחרות – אילו שירותים מספקת התשתית שיחסכו לנו קוד וזמן? שימו לב שחלק מהחבילות מכילות שירותים כמו ניהול בסיס נתונים, cache, ניהול משתמשים ו-sessions, וכדומה, ועבור חבילות אחרות השירותים האלו קיימים כתוספים (פלאגינים).
  • כמה קשה יהיה לתחזק את הקוד לאורך זמן?
  • באיזה שרת נשתמש ב-production? האם קיימת אינטגרציה בין תשתית ה-web לשרת?

נדמיין לדוגמה שאנו ניגשים לפתח אפליקציה שממומשת כמעט כולה ב-client וצד השרת שלה פשוט במיוחד. במקרה זה העלות של שימוש בספרייה כמו django, המכילה מעל 3000 קבצים ודורשת לפחות 10 קבצים בפרוייקט שלנו רק כדי להתחיל לעבוד, היא גבוהה ביחס לתועלת הנמוכה שנקבל מכיוון שלא נשתמש בכמעט כלום ממה שיש ל-django להציע.

חלק נוסף מהעלות נגזר גם מגודל קהילת המפתחים שמשתמשים בה, שואלים ועונים על שאלות ומפתחים עבורה תוספים והרחבות. למשל נכון לכתיבת שורות אלו לחבילת flask יש כ-10,000 שאלות ב-Stack Overflow, ולעומתה על CherryPy נשאלו 960 בלבד.

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

  • מנוע templates
  • ORM – מידול וגישה לבסיסי נתונים (SQL או NoSQL)
  • ניהול משתמשים והרשאות
  • מנוע cache
  • תמיכה במספר שפות
  • Authentication
  • Multitheading
  • תמיכה בקבצים סטאטיים.
  • DB Migrations
  • ניהול קבצים סטאטיים
  • כלי בדיקה

קיימות הרבה השוואות בין חבילות web בפייתון המשוות עשרות חבילות. אני לא מתיימר להכיר את כולן או לבצע השוואות מקיפות יותר מאלו הקיימות, ולכן אציג כאן רק את הבחירות שלי, שנבחרו לפי השיקולים הנ"ל.

לפרוייקטים קטנים – Bottle

הרבה פעמים אני מוצא את עצמי צריך לכתוב אפליקציה מבוססת javascript הדורשת מעט מאוד צד שרת, או אפילו סתם קוד python שהפלט שלו הוא טקסט בעברית שלא ניתן להציג על command line בצורה נוחה. במקרים כאלו אני רוצה להרים שרת web בפחות מדקה, ואין כמו bottle עבור משימה זאת.

חבילת Bottle היא מיקרו תשתית (micro-framework), המכילה למעשה קובץ אחד בלבד – bottle.py, שבו ממומשת תשתית web שלמה ואפילו שרת לבדיקה. כדי לכתוב תוכנית בסיסית בעזרת bottle, לא נדרשות יותר מחמש שורות הקוד הבאות:

from bottle import route, run, template

@route('/hello/<name>')
def index(name):
    return template('<b>Hello {{name}}</b>!', name=name)

run(host='localhost', port=8080)

למרות היותה מיקרו-תשתית, bottle תומכת בכל מה שצריך כדי לבנות פרוייקטים קטנים: מנוע routing, מנגנון templating, גישה לאובייקטי request ו-response ותמיכה בהעלאת קבצים. בנוסף מכילה bottle לא מעט adapters שיעזרו להתממשק עם חבילות מפורסמות כמו Jinja2, ומעל 15 שרתי http. להלן דוגמה איך בתוספת של שורת קוד אחת בלבד ניתן להשתמש בתבניות של Jinja2:

from bottle import jinja2_view, route
 
 @route('/', name='home')
 @jinja2_view('home.html', template_lookup=['templates'])
 def home():
     return {'title': 'Hello world'}

עבור bottle נכתב גם אוסף קטן ויעיל של פלאגינים המתממשקים לשירותים כמו memcached, mongodb, SQLAlchemy, SQLite ועוד. כל אלו הופכים את bottle למתאימה מאוד עבור פרויקטים בסיסיים הדורשים מספר מצומצם של שירותים חיצוניים.

העצה שלי היא להשתמש ב-bottle לפרוייקטים שאתם לא צופים שיהפכו למפלצות. אבל גם אם כן – כל עוד תקפידו להשתמש בחבילות צד שלישי נפוצות כמו Jinja2 ו-memcached, לא אמורה להיות בעיה לשדרג בעתיד לתשתית מתקדמת יותר.

לא הייתי מנצל את bottle עבור פרוייקטים הדורשים יותר מדי פלאגינים וכלים חיצוניים, כי כמובן שעדיף אוסף של ספריות שנבדקו ביחד כגון tornado או Django. בנוסף, פרוייקטים גדולים דורשים לרוב מבנה מסודר כדי שהקוד לא יהפוך לבלאגן רציני, גם כאן תשתית bottle לא מספקת מענה, מכיוון שהיא לא מספקת מוסכמה למבנה התיקיות של פרוייקט.

חלופה נפוצה וטובה לא פחות ל-bottle היא Flask, שגם היא micro framework, והקהילה שלה גדולה יותר. הנה השוואה של עוד כמה מיקרו תשתיות.

לפרוייקטים בינוניים וגדולים – Django – המלכה הבלתי מעורערת

Django היא ללא ספק גולת הכתר של חבילות תשתיות ה-web הפייתוניות. עם היסטוריה של 12 שנים, מעל 100k (!) שאלות ב-SO וקהילת מפתחים ותורמים ענקית ופעילה, תוכלו למצוא מענה מיידי כמעט לכל שאלה. מערכת ה-ORM של Django ידועה כמקיפה ביותר, תומכת במספר נאה של בסיסי נתונים מבוססי SQL, ומשמשת תמיד כדוגמה לשימוש מופתי ב-Metaclasses.

בדיוק כמו ההוא מהסרט, Django משוחררת מכל מעצורים ומאפשרת לפתח כמעט כל סוג של פרוייקט רשת, גם בקנה מידה ענקי. הגישה של Django היא "הסוללות כלולות", כלומר מפתחיה משתדלים להכניס לתוכה את כל מה שצריך בשביל רוב הפרוייקטים: ORM מפואר, cache, שפת templates שלמה, מערכת ניהול DB, ניהול sessions, ניהול משתמשים ואותנטיקציה, יכולות אבטחה, תמיכה במספר אתרים במקביל, תמיכה בקבצים סטאטיים, תמיכה במספר שפות, ועוד. ואם בכל זאת מצאתם משהו שלא כלול, בוודאי תשמחו לדעת שבמשך השנים פותחו ל-Django מאות, אם לא אלפי "אפליקציות" הרחבה, ביניהן NoSQL DB Backends, תשתיות REST API, חבילות אותנטיקציה מסוגים שונים, חבילות אבטחה, ואפילו מערכות CMS ובלוגים שלמות.

התיעוד של Django הינו מקיף וברור, מתעדכן באופן קבוע ומאפשר להבחין בין הגרסאות השונות בקלות. בנוסף הקהילה קשובה ורספונסיבית מאוד לבקשות המשתמשים.

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