למה Javascript מרגישה לחלקנו כמו סינית?

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

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

בתור מישהו שכותב Javascript כבר שנים רבות, הבעיה הזאת תמיד סיקרנה אותי. הרי התחביר של Javascript דומה מאוד לשפות אחרות (כמו C++) ויכולותיה "out of the box" מצומצמות מאוד, אז מדוע מתכנתים רבים מתקשים בהבנתה? אחרי מחשבה רבה אני מאמין שמצאתי את שורש הבעיה.

אסינכרוניות מובנית

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

ניקח לדוגמה את הפסאודו קוד הבא, הכתוב בסגנון קלאסי:

name = wait_for_user_input()
print "Welcome " + name

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

אחד המאפיינים המעניינים והחשובים ביותר של שפת Javascript הוא הריצה ב-thread אחד בלבד. כלומר אם היינו כותבים קוד כזה ב-JS, כל ממשק המשתמש (אתר, אפליקציה וכד') היה נתקע עד לקבלת הקלט.

אז איך בכל זאת מאפשרת שפת Javascript פעולות המצריכות המתנה (למשתמש, לשרת וכד')? התשובה לכך היא, לדעתי, המפתח להבנת Javascript.

פונקציות Callback ותכנות מבוסס אירועים

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

function handle_user_input(name) {
    console.log("Welcome " + name);
}

wait_for_user_input(handle_user_input);

למעשה שינינו שלושה דברים:

  1. הגדרנו פונקציה חדשה המטפלת בקלט שהגיע מהמשתמש. או במילים אחרות, הגדרנו event handler, כאשר האירוע (event) בו הוא מטפל הוא הגעת קלט מהמשתמש.
  2. העברנו את הפוקציה החדשה בתור פונקציית callback לפונקציה wait_for_user_input. כלומר, רק כאשר יתקבל הקלט מהמשתמש, הפונקציה שלנו שלנו תיקרא ותכתוב את הקלט ללוג.

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

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

wait_for_user_input(function(name) {
    console.log("Welcome " + name);
})

אני מאמין שהשינוי הקטן הזה הוא המפתח להבנת קוד Javascript. אז מה קרה פה? במקום להגדיר בנפרד את פונקציית ה-callback ולתת לה שם, הפכנו אותה לפונקציה אנונימית (anonymous function) והעברנו אותה ישירות לפונקציה wait_for_user_input.

השינוי הזה הפך את הקוד למשהו שלא דומה לקוד של אף שפת תכנות קלאסית!

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

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

document.getElementById("btn").addEventListener("click", function(evt) {
  if (validate_data()) {
    do_ajax_call(function (data) {
      if (data.success) {
        document.getElemenetById("next").addEventListener("click", function() {
          window.location.href = '/thank-you';
        });
      } else {
        console.log("error: " + data.msg);
      }
    });
  }
});

לא עוד Callback Hell

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

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

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

fetch_data_from_server(function(data) {
    // handle data
    fetch_more_data_from_server(more_data) {
        // handle more data
    }
});

ניתן שמות לפונקציות האנונימיות ונחלץ אותן החוצה:

fetch_data_from_server(handle_data);

function handle_data(data) {
    // handle data
    fetch_more_data_from_server(handle_more_data);
}

function handle_more_data(more_data) {
    // handle more data
}

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

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

Callback Hell

לקריאה נוספת:

תהליך בנייה לקוד צד לקוח – למה צריך את זה ואיך מייצרים אחד?

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

  • שפות מתקמפלות, כגון c ונגזרותיה, Java, ועוד. שפות אלו מצריכות תהליך הידור (קומפילציה), הממיר אותן לקוד בשפה נמוכה יותר כגון שפת מכונה או שפת ביניים, אותה ניתן להריץ ישירות על מכשירי היעד.
  • שפות סקריפט, כגון Python, PHP, Javascript וכד'. שפות אלו לא צריכות קומפילציה, והקוד שלהן נשלח כמו שהוא לשטח, שם הוא מורץ ע"י interpreter.

לאחרונה הגבול בין שתי הקבוצות החל להיטשטש. ניקח לדוגמה את פייסבוק, שם כתבו מערכת להמרת קוד ה-PHP שלהם לקוד C++ לטובת שיפור ביצועים. גם אם הקוד עדיין מפותח בשפת סקריפט, בפועל הוא מומר ומורץ בשטח כקוד בשפה אחרת. התהליך האוטומטי המבצע פעולות על הקוד כדי להכינו לפרודקשן נקרא תהליך בנייה (בִּילְד).

במאמר זה נתמקד בתהליך בנייה עבור קוד צד לקוח (Front End) הכתוב בשפות Javascript, CSS ו-HTML, נבין איך תהליך כזה עוזר לנו, ונכיר טכנולוגיות העוזרות לנו לממש אותו.

למה צריך תהליך בנייה לקוד צד לקוח?

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

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

איחוד קבצי קוד

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

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

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

טרנספילציה

כאשר משתמשים בשפות הנגזרות מ-Javascript כגון CoffeeScript, JSX או Dart, רצוי להמיר את הקוד ל-JS לפני ההעלאה לאתר ולא בזמן הריצה (אחרת יש צורך בטעינת ספריות js נוספות והרצתן). תהליך ההמרה, הנקרא טרנספילציה או טרנסקומפילציה, מבוצע בתהליך הבנייה, וניתן לבצע אותו גם עבור שפות המומרות ל-CSS כגון less או Sass.

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

מזעור נפח הקוד

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

כלים רבים, ביניהם Closure Compiler, UglifyJS ו-Clean-css, ממזערים את הקוד ע"י שיטות שונות כמו הסרת רווחים מיותרים, שינוי שמות משתנים מקומיים, מחיקת הערות ועוד, וזאת ללא פגיעה בתפקוד הקוד. כלים אלו גם יוודאו את תקינות הקוד ויציפו שגיאות תחביר.

לדוגמה, נניח שהקוד שלנו נראה כך:

// A simple function.
function hello(longName) {
  alert('Hello, ' + longName);
}
hello('New User');

לאחר הרצה של Closure Compiler כחלק מתהליך הבנייה, הקוד ימוזער לקוד הבא, הקצר ממנו בחצי:

function hello(a){alert("Hello, "+a)}hello("New User");

מדובר בחיסכון של 50% מנפח הקוד, שיכול להגיע למאות KB ואף יותר באפליקציות גדולות.

ניתוח סטאטי של הקוד

ישנן טעויות רבות בקוד שניתן לגלות בנקל ע"י מעבר אוטומטי על הקוד:

  • שימוש במשתנים לא מוגדרים
  • קוד שאינו בשימוש
  • קוד מת שלעולם לא יורץ
  • שימוש מסוכן באופרטורים
  • שמות משתנים ופונקציות מבלבלים ושאינם מתאימים למוסכמות

כמעט עבור כל שפות התכנות קיימים כלים המבצעים ניתוח סטאטי של הקוד ומגלים את השגיאות הנ"ל ועוד רבות אחרות. עבור JS ו-CSS ניתן להשתמש בכלים JSHint ו-CSS Lint בהתאמה.

שינוי שם קובץ הקוד הסופי

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

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

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

כמובן שתהליך קביעת שם הקובץ מתבצע אוטומטית בתהליך הבנייה, כמו גם עדכון קבצי ה-HTML המתאימים כך שידעו איזה קובץ לטעון. הכלי שאני משתמש בו הוא gulp-rev, הקובע את שם הקובץ לפי חישוב hash על הקוד.

הרצת בדיקות אוטומטיות

במידה וכתבנו בדיקות אוטומטיות, נרצה להריץ אותן לפי העלאת הקוד לאתר.

איך מייצרים תהליך בנייה?

במידה ותהליך הבנייה שלכם פשוט ומכיל שלבים בודדים בלבד, תוכלו לממש אותו בעצמכם בשפת הסקריפט המועדפת עליכם, או אפילו ע"י shell script. אך עבור תהליכים מורכבים יותר, קיימים כלים יעודיים ומעולים שנכתבו למטרה זאת, רובם מבוססי Node.js. (שימו לב – זה לא אומר שאתם צריכים שרת Node.js כדי להריץ את האתר שלכם, אלא רק להתקין Node.js על מחשב הפיתוח). שני הכלים הנפוצים ביותר נכון לכתיבת שורות אלו הם:

Grunt

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

Grunt מבוסס על קונפיגורציה, כלומר לאחר התקנת הפלאגינים הדרושים, תצטרכו להגדיר מה אתם רוצים לעשות באמצעות JSON, בקובץ JS הנקרא Gruntfile. דוגמה לקובץ Gruntfile המריץ את Uglify Js:

grunt.initConfig({
  pkg: grunt.file.readJSON('package.json'),
  uglify: {
    options: {
      banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
    },
    build: {
      src: 'src/<%= pkg.name %>.js',
      dest: 'build/<%= pkg.name %>.min.js'
    }
  }
});

Gulp

בדומה ל-Grunt, גם גאלפ מתבסס על אוסף רחב של פלאגינים. אך בשונה ממנו, גאלפ מצריך כתיבת תהליך הבילד בקוד ומתבסס על Node.js streams. בפועל מדובר בקוד פשוט מאוד, שאת רובו ככולו ניתן למצוא מוכן בתיעוד הפלאגינים. לדוגמה:

gulp.task('css_lint', function() {
 return gulp.src(['src/main.less'])
   .pipe(csslint('./.csslintrc'))
   .pipe(csslint.reporter())   
   .pipe(concat(module_name + '.css'))   
   .pipe(autoprefixer());
});

אני אשאיר לכם את הבחירה בין השניים, אך אציין שהבחירה האישית שלי היא Gulp, מכיוון שהוא חדש יותר, ולדעתי התחביר שלו נוח יותר.

דוגמה לתהליך בנייה מלא

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

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

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

 

6 טכניקות שיזניקו את קוד ה-web שלכם אל העתיד

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

לדוגמה, תחביר חדש ושימושי של javascript הוא arrow functions:

var func = (x, y) => x + y - 1;
func(3, 4); // 6

נכון לכתיבת שורות אלו השימוש ב-arrow functions לא אפשרי ב-Internet Explorer (באף גרסה), ואף יגרום ל-syntax error. הדבר מונע לחלוטין שימוש ביכולת זו באתר שאמור לתמוך ב-IE (אלא אם בוחרים להתעלם מבערך 10% מהגולשים שלנו).

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

#1 – הכרת השטח

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

Can I use

האתר המדהים הזה מחזיק רשימה מעודכנת של כל הפיצ'רים החדשים של HTML, CSS ו-Javascript ועבור כל אחד מהם מציג באיזה פלטפורמות הוא נתמך, ואיזה בעיות ידועות יש איתו.

למשל בתמונה למעלה ניתן לראות ש-CSS3 3D Transforms נתמכים בכל הדפדפנים למעט IE בגרסה 9 ומטה ו-Opera Mini. כעט ניתן להחליט האם להשתמש בפיצ'ר זה ולפגוע בנראות האתר בדפדפנים הישנים או לא.

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

ECMAScript Compatibility Table

בשונה משפות תכנות קונבנציונליות שיוצאות לשוק בגרסאות, Javascript, או בשמה החדש ECMAScript, היא אוסף גדול של יכולות בלתי תלויות. כל פלטפורמה (דפדפן, mobile os, מנוע JS) בוחר לממש חלק מהפיצ'רים האלו בסדר שרירותי כלשהו, כלומר תמיכה ביכולת אחת לא בהכרח מעידה על תמיכה באחרת.

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

ECMAScript Compatibility Matrix
ECMAScript Compatibility Matrix

בדומה ל-Can I use, ניתן להשתמש במידע זה כדי להחליט באילו יכולות שווה לנו להשתמש, באילו לא, ולאילו אפשר לחפש חלופות.

#2 – Transpiling

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

Javascript Transpilers

קיימים מספר טרנספיילרים עבור javascript, והמועדף עליי הוא Babel (בעבר 6to5). כלי זה ממיר שימוש בפיצ'רים חדשים של JS לקוד המבצע את אותה פעולה בדיוק (או בערך), אך ע"י שימוש בקוד JS הנתמך בכל הדפדפנים והפלטפורמות.

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

CSS Transpilers

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

div {
  appearance:button;
  -moz-appearance:button; /* Firefox */
  -webkit-appearance:button; /* Safari and Chrome */
}

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

תהליך בנייה

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

#3 – Polyfills

יכולות מסויימות של Javascript או HTML הן מורכבות מדי כדי לממש בתוך טרנספיילר, למשל SVG, Canvas, HTML Storage ועוד. כדי שבכל זאת נוכל להשתמש בהן, נכתבו עבורן תחליפים עבור דפדפנים ישנים. תחליפים אלו נקראים polyfills (ולעיתים shims). קיימים Polyfills כמעט עבור כל תכונה חדשה של HTML או JS, ורשימה די מקיפה שלהם ניתן למצוא כאן.

ברוב המקרים polyfill לא ירוץ אם הוא יזהה שהיכולת שהוא אמור להחליף כבר קיימת בדפדפן הנוכחי, ולכן זה בטוח להכיל את ה-polyfills בקוד שלנו תמיד. אך שימו לב ש-polyfills הם בסופו של דבר קוד JS, ושימוש מופרז בהם יכול להגדיל את הקוד שלכם משמעותית. אם לא אכפת לכם לבצע בקשת HTTP נוספת, תוכלו להשתמש בשירות polyfills.io, המוריד את ה-polyfills הנדרשים לפי ה-user agent של הדפדפן, וכך יכול לחסוך רוחב פס יקר.

#4 – זיהוי בזמן אמת ומימוש חלופות

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

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

לאחר ריצה של כל בדיקה תתווסף מחלקה (class) לתגית ה-html של האתר שלכם, שתאפשר לכם לבנות התנהגות שונה, אפילו בקוד ה-CSS עצמו, במידה ויכולת מסויימת ממומשת או לא. כמובן שניתן לגשת לתוצאות הבדיקות גם באמצעות JS.

#5 – בדיקות, בדיקות, בדיקות

אז סיימתם לבנות את האתר שלכם, ווידאתם שהוא עובד מעולה ב-Chrome ,Firefox ו-IE בגרסאות המותקנות לכם על המחשב. אבל מה עם גרסאות דפדפן ישנות? ומובייל? ודפדפנים איזוטריים?

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

  • אתרים כמו Browsershots (ויש עוד עשרות כאלו) יצלמו עבורכם את האתר שלכם בכל דפדפן שתבחרו. כך תוכלו לזהות במהירות היכן יש בעיות נראות לעין ולהתמקד בדפדפנים הבעייתיים.
  • השירות BrowserStack מאפשר לכם להתחבר לעשרות גרסאות של דפדפנים על כל סוגי מערכות ההפעלה (כן, גם מובייל) ולבדוק בפועל את האתר שלכם עליהם. ניתן אפילו לבדוק את האתר כאשר הוא רץ על המחשב שלכם!!
  • אם אין לכם כח לבדוק ידנית כל פעם, ואין לכם צוות QA פנימי, חברת Rainforest QA מספקת שירותי QA as a Service, ומאפשרת לכם להגדיר בדיקות ידניות, שיבוצעו ע"י בודקים אנושיים באופן מיידי. אגב, הם משתמשים ב-Mechanical Turk.
  • ואם יש לכם קצת זמן פנוי, אולי שווה לכם לכתוב בדיקות אוטומטיות באמצעות Selenium. ישנם שירותים (BrowserStack ביניהם) המאפשרים לכם להריץ את בדיקות ה-Selenium שלכם בענן על עשרות דפדפנים שונים.

#6 ואחרון – ניטור שגיאות

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

אך זה שמשהו קורה אי שם אצל הלקוח לא אומר שאי אפשר לדעת עליו ולטפל בו!

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

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

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

לסיכום

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