URLPattern מביא ניתוב לפלטפורמת האינטרנט

גישה ליצירת סטנדרטים לתרחישים נפוצים לדוגמה של התאמה לדפוסים.

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

אין סטנדרט אחד מוחלט, אבל מפתחי אתרים משתמשים בתחביר משותף לביטוי של דפוסי ניתוב של כתובות URL שיש להם מאפיינים משותפים רבים עם regular expressions, אבל עם תוספות ספציפיות לדומיין, כמו אסימונים לפלחי נתיב תואמים. frameworks פופולריות בצד השרת כמו Express ו-Ruby on Rails משתמשות בתחביר הזה (או משהו שקרוב מאוד אליו), ומפתחי JavaScript יכולים להשתמש במודולים כמו path-to-regexp או regexpparam כדי להוסיף את הלוגיקה הזו לקוד שלהם.

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

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

תמיכה בדפדפנים ו-polyfills

URLPattern מופעל כברירת מחדל ב-Chrome וב-Edge מגרסה 95 ואילך.

הספרייה urlpattern-polyfill מאפשרת להשתמש בממשק URLPattern בדפדפנים או בסביבות כמו Node שאין בהם תמיכה מובנית. אם אתם משתמשים ב-polyfill, חשוב להשתמש בזיהוי תכונות כדי לוודא שאתם טוענים אותו רק אם אין תמיכה בסביבה הנוכחית. אחרת, תאבדו את אחת מהיתרונות העיקריים של URLPattern: העובדה שבסביבות התמיכה לא צריך להוריד ולנתח קוד נוסף כדי להשתמש בו.

if (!(globalThis && 'URLPattern' in globalThis)) {
  // URLPattern is not available, so the polyfill is needed.
}

תאימות תחביר

אחד העקרונות המנחים של URLPattern הוא להימנע מיצירת משהו חדש לגמרי. אם אתם כבר מכירים את תחביר הניתוב שבו משתמשים ב-Express או ב-Ruby on Rails, לא צריך ללמוד שום דבר חדש. אבל בגלל ההבדלים הקלים בין תחביר בספריות ניתוב פופולריות, היה צריך לבחור בתחביר הבסיס כתחביר הבסיס, והמתכננים של URLPattern החליטו להשתמש בתחביר התבניות של path-to-regexp (אבל לא את פלטפורמת ה-API שלו) בתור נקודת ההתחלה.

ההחלטה הזו התקבלה לאחר התייעצות מעמיקה עם המנהל הנוכחי של path-to-regexp.

הדרך הטובה ביותר להכיר את הליבה של התחביר הנתמך היא לעיין במסמכי התיעוד של path-to-regexp. אתם יכולים לקרוא את המסמכים שמיועדים לפרסום ב-MDN באתר הנוכחי שלהם ב-GitHub.

תכונות נוספות

התחביר של URLPattern הוא קבוצת-על של מה שנתמך ב-path-to-regexp, כי URLPattern תומך בתכונה לא נפוצה בקרב ספריות ניתוב: התאמה למקורות, כולל תווים כלליים לחיפוש בשמות מארחים. רוב ספריות הניתוב האחרות מטפלות רק בpathname, ולפעמים בחלק של החיפוש או ה-hash בכתובת ה-URL. הם אף פעם לא צריכים לבדוק את החלק של המקור בכתובת ה-URL, כי הם משמשים רק לניתוב מאותו מקור באפליקציית אינטרנט עצמאית.

התייחסות למקורות פותחת את הדלת לתרחישים נוספים לדוגמה, ניתוב בקשות ממקורות שונים בתוך פונקציית הטיפול באירועים fetch של service worker. אם מעבירים רק כתובות URL ממקור זהה, אפשר למעשה להתעלם מהתכונה הנוספת הזו ולהשתמש ב-URLPattern כמו בספריות אחרות.

דוגמאות

בניית הדפוס

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

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

const p = new URLPattern({
  protocol: 'https',
  username: '',
  password: '',
  hostname: 'example.com',
  port: '',
  pathname: '/foo/:image.jpg',
  search: '*',
  hash: '*',
});

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

ב-constructor יש כמה קיצורי דרך לשימוש פשוט יותר. השמטה מוחלטת של search ו-hash, או של מאפיינים אחרים, זהה להגדרתם כתובת ה-wildcard '*'. אפשר לפשט את הדוגמה שלמעלה כך:

const p = new URLPattern({
  protocol: 'https',
  username: '',
  password: '',
  hostname: 'example.com',
  port: '',
  pathname: '/foo/:image.jpg',
});

כקיצור דרך נוסף, אפשר לספק את כל המידע על המקור בנכס אחד, baseURL, שמוביל אל

const p = new URLPattern({
  pathname: '/foo/:image.jpg',
  baseURL: 'https://example.com',
});

כל הדוגמאות האלה מבוססות על ההנחה שהתרחיש לדוגמה שלכם כולל מקורות תואמים. אם אתם רוצים להתאים רק לחלקים האחרים של כתובת ה-URL, לא כולל המקור (כמו שקורה בתרחישים רבים של ניתוב 'מסורתי' למקור יחיד), תוכלו להשמיט את פרטי המקור לגמרי ולספק רק שילוב כלשהו של המאפיינים pathname,‏ search ו-hash. כמו קודם, נכסים שהושמטו יטופלו כאילו הוגדרו לדפוס התו הכללי לחיפוש *.

const p = new URLPattern({pathname: '/foo/:image.jpg'});

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

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

const p = new URLPattern('https://example.com/foo/:image.jpg?*#*');

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

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

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

// p1 and p2 are equivalent.
const p1 = new URLPattern('/foo', location.origin);
const p2 = new URLPattern({
  protocol: location.protocol,
  hostname: location.hostname,
  pathname: '/foo',
  search: '',
  hash: '',
});

// p3 and p4 are equivalent.
const p3 = new URLPattern('/foo?*#*', location.origin);
const p4 = new URLPattern({
  protocol: location.protocol,
  hostname: location.hostname,
  pathname: '/foo',
});

חשוב גם לדעת שניתוח דפוס מחרוזת לרכיבים שלו עלול להיות לא ברור. יש תווים, כמו :, שנמצאים בכתובות URL, אבל יש להם גם משמעות מיוחדת בתחביר של התאמת התבניות. כדי להימנע מהמצב הזה, המאגר (constructor) של URLPattern מניח שכל אחד מהתווים המיוחדים האלה הוא חלק מתבנית, ולא חלק מכתובת ה-URL. אם רוצים שתו מסוים שיכול להתפרש כחלק מכתובת ה-URL יפורש כחלק ממנה, צריך להשתמש בתווית בריחה (escape) \` character. For example, the literal URLabout:blankshould be escaped as'about\:blank'` כשהוא מסופק כמחרוזת.

שימוש בקו ביטול הנעילה

אחרי שיוצרים URLPattern, יש שתי אפשרויות לשימוש בו. ה-methods test() ו-exec() מקבלות את אותו קלט ומשתמשות באותו אלגוריתם כדי לבדוק אם יש התאמה, ורק הערך המוחזר שלהן שונה. test() מחזירה את הערך true אם יש התאמה לקלט הנתון, ואת הערך false במקרים אחרים. הפונקציה exec() מחזירה מידע מפורט על ההתאמה, יחד עם קבוצות לחילוץ, או את הערך null אם אין התאמה. בדוגמאות הבאות אפשר להשתמש ב-exec(), אבל אפשר להחליף את הערך ב-test() בכל אחת מהן אם רוצים רק ערך מוחזר בוליאני פשוט.

אחת מהדרכים להשתמש בשיטות test() ו-exec() היא להעביר מחרוזות. בדומה למה שה-constructor תומך בו, אם מציינים מחרוזת אחת, היא צריכה להיות כתובת URL מלאה, כולל המקור. אם מציינים שתי מחרוזות, המחרוזת השנייה נחשבת כערך של baseURL והמחרוזת הראשונה נבדקת ביחס לבסיס הזה.

const p = new URLPattern({
  pathname: '/foo/:image.jpg',
  baseURL: 'https://example.com',
});

const result = p.exec('https://example.com/foo/cat.jpg');
// result will contain info about the successful match.
// const result = p.exec('/foo/cat.jpg', 'https://example.com')
// is equivalent, using the baseURL syntax.

const noMatchResult = p.exec('https://example.com/bar');
// noMatchResult will be null.

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

const p = new URLPattern({pathname: '/foo/:image.jpg'});

const result = p.exec({pathname: '/foo/:image.jpg'});
// result will contain info about the successful match.

כשמשתמשים ב-exec() ב-URLPattern שמכיל תווים כלליים לחיפוש או אסימונים, הערך המוחזר יספק מידע על הערכים התואמים בכתובת ה-URL שהוזנה. כך תוכלו לחסוך את הצורך לנתח את הערכים האלה בעצמכם.

const p = new URLPattern({
  hostname: ':subdomain.example.com',
  pathname: '/*/:image.jpg'
});

const result = p.exec('https://imagecdn1.example.com/foo/cat.jpg');
// result.hostname.groups.subdomain will be 'imagecdn1'
// result.pathname.groups[0] will be 'foo', corresponding to *
// result.pathname.groups.image will be 'cat'

קבוצות אנונימיות עם שם

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

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

אפשר להשתמש במיקומי מערך כדי לגשת לערכים של התאמה אנונימית לדפוס. אם יש כמה דפוסים אנונימיים, האינדקס 0 ייצג את הערך התואם לדפוס שמשמאל ביותר, והאינדקסים 1 ואינדקסים נוספים ישמשו לדפוסים הבאים.

כשמשתמשים בקבוצות בעלות שם בתבנית, ההתאמות יוצגו כמאפיינים ששמותיהם תואמים לשמות של כל קבוצה.

תמיכה ב-Unicode ותקינה

URLPattern תומך בתווי Unicode בכמה דרכים שונות.

  • קבוצות בעלות שם, כמו :café, יכולות להכיל תווים של Unicode. הכללים שמשמשים למזהי JavaScript תקינים חלים על קבוצות בעלות שם.

  • טקסט בתוך תבנית יקודד באופן אוטומטי לפי אותם כללים שמשמשים לקידוד כתובות ה-URL של הרכיב המסוים. תווי Unicode ב-pathname יקודרו לפי אחוזים, כך שתבנית pathname כמו /café תנורמליזציה באופן אוטומטי ל-/caf%C3%A9. תווי Unicode ב-hostname מקודדים באופן אוטומטי באמצעות Punycode, במקום קידוד באחוזים.

  • קבוצות של ביטויים רגולריים חייבות להכיל רק תווי ASCII. תחביר הביטוי הרגולרי מקשה על קידוד אוטומטי של תווים ב-Unicode בקבוצות האלה, וגם לא בטוח לעשות זאת. אם רוצים להתאים תו של Unicode בקבוצה של ביטוי רגולרי, צריך לבצע קידוד אחוזים ידני, למשל (caf%C3%A9) כדי להתאים ל-café.

נוסף לקידוד תווי Unicode, הפונקציה URLPattern מבצעת גם נירמול של כתובות URL. לדוגמה, /foo/./bar ברכיב pathname ימוקם ב-/foo/bar המקביל.

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

סיכום של כל המידע

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

משוב ותוכניות עתידיות

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

תמיכה בתבניות

בספרייה path-to-regexp יש פונקציה compile() function שמשנה את התנהגות הניתוב. compile() לוקחת את התבנית והערכים של ה-placeholders של האסימון, ומחזירה מחרוזת של נתיב כתובת ה-URL עם הערכים האלה.

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

הפעלת תכונות עתידיות של פלטפורמת אינטרנט

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

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

אישורים

במסמך ההסבר המקורי מופיעה רשימה מלאה של תודות.