צינור הנכסים

איך CSS ו-JS מגיעים לדפדפן ב-WizzoCMS - add_asset, קומפילציית SCSS דרך כתובות .scss.css, קבוצות minify, סקריפטים ו-tag manager, ו-attaches_version לניקוי מטמון.

⏱ 7 דק' קריאה 1242 מילים ערוך דף זה ב-GitHub

הנכסים של האתר (CSS, JS, SCSS) לא נכתבים ידנית ל-<head>. הקוד רושם אותם ב-PAGE, והליבה מפיקה מהם את התגים, מוסיפה גרסה לכתובת, מקמפלת SCSS ומאחדת קבצים לקבוצות. בעמוד הזה: איך רושמים נכסים, איך עובד .scss.css, איך בונים קבוצות minify, איפה מנהלים סקריפטים חיצוניים, ואיך הגרסה (attaches_version) מנקה מטמונים.

רישום נכס#

הרישום נעשה מקוד ה-PHP של ה-theme, של הבקר או של application/includes. החתימות המלאות ב-PAGE API וב-ref-page; כאן מה שחשוב לצינור:

// בתוך TEMPLATE_default::index() או בבקר
PAGE::add_asset("themes/default/style.scss.css");                 // head, בסוף הרשימה
PAGE::add_asset("themes/default/app.js", false, "body_end");      // סוף ה-body
PAGE::add_asset_advanced("themes/default/slider.js", [
    "area"  => "body_end",
    "defer" => true,
]);
PAGE::add_asset_advanced("themes/default/chat.js", [
    "after_user_intraction" => true,   // נטען רק בלחיצה או במקש הראשון
]);

כל נכס נפלט כתג בהתאם לסיומת. לכתובת יחסית מתווספים פרוטוקול ו-original_site_url, ולכל כתובת (CSS וגם JS) מתווסף ?ver=<attaches_version> (MISC::add_querystring_var), כך שהדפדפן וה-CDN מביאים גרסה חדשה אחרי שהגרסה עולה.

אפשרותהתנהגות ב-CSSהתנהגות ב-JS
asyncpreload ואז החלפה ל-media=printמאפיין async
deferלא רלוונטימאפיין defer
printהתוכן עובר FILES::scss($src) ומוטמע ב-<style> (אם נכשל: תג link)לא רלוונטי
after_user_intractionהנכס לא נפלט ב-HTML, ונטען ע"י apply_after_interaction אחרי האינטראקציה הראשונהכנ"ל
הערה

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

SCSS: כתובות .scss.css#

הדפדפן לא יודע לטעון SCSS, אז הליבה מגישה קבצי SCSS מתחת לכתובת שנגמרת ב-.scss.css. ה-ROUTER מזהה את הסיומת ומעביר ל-minify::scss:

כתובתמה קורה
/assets/<path>.scss.cssהמקור נקרא מ-<core_path>/assets/<path> (ספריות הליבה)
/<path>.scss.cssהמקור נקרא מתיקיית האתר, <path>.scss

כלומר ב-HTML כותבים themes/default/style.scss.css, ועל הדיסק יושב themes/default/style.scss. FILES::scss($file) עושה את העבודה:

  1. מסיר query ו-hash מהכתובת. כתובת חיצונית (עם דומיין אחר) מחזירה false.
  2. מאתר את קובץ המקור. אם אין .scss אבל יש .css פיזי באותו שם, מוחזר תוכן ה-CSS כמות שהוא. קובץ .css רגיל נקרא ישירות.
  3. בודק מטמון: cache/<SERVER_NAME>/scss/<file>ver=<attaches_version>.css. קובץ מטמון ריק נחשב שריד מכתיבה שנקטעה ומקומפל מחדש.
  4. מקמפל עם ScssPhp בפלט COMPRESSED, כשתיקיית המקור היא נתיב ה-@import. כך @import "variables" בתוך style.scss מוצא את _variables.scss שלידו.
  5. מתקן url(...) יחסיים לנתיבים מוחלטים משורש האתר (נתיבים עם http, data: או / בתחילתם נשארים).
  6. כותב את המטמון ומחזיר את ה-CSS. כשל קומפילציה (חריגה של ScssPhp) מחזיר false.
// themes/default/style.scss
@import "variables";
@import "header";

.hero {
    padding-inline: 1rem;
    background: $brand;
}
שימו לב

כשהקומפילציה נכשלת (קובץ חסר, שגיאת תחביר, @import שלא נמצא) minify::scss עונה 500 עם Cache-Control: no-store וגוף scss compile failed. זה מכוון: תשובה ריקה עם 200 הייתה נשמרת ב-CDN לפי ה-ver והאתר היה נשאר בלי עיצוב. אם האתר נראה בלי סגנונות, פתחו את כתובת ה-.scss.css ישירות ובדקו את הסטטוס.

טיפ

בקוד חדש ב-.scss של האתר השתמשו בתכונות לוגיות (padding-inline, margin-inline-start, inset-inline-end) ולא ב-left ו-right. האתר יכול להיות RTL.

קבוצות minify#

בעמוד רגיל כל נכס הוא בקשה נפרדת. קבוצת minify מאחדת כמה קבצים לבקשה אחת, minify/<id>.js או minify/<id>.css. הקבוצות מוגדרות בשתי טבלאות:

טבלהשדות עיקריים
minify_groupsid, name, type (js או css), area (head, body_start, body_end), async, theme (ריק = כל ה-themes), ord
minify_filesid, url (נתיב הקובץ מתיקיית האתר), group_id, ord

הניהול בפאנל minify של האדמין (ADMINMODULE_minify): רשימת קבוצות, ולחיצה כפולה על קבוצה פותחת את רשימת הקבצים שלה. שינוי בפאנל מנקה את מפתחות המטמון minify_groups ו-minify.

בטעינת העמוד, PAGE::load קורא את שתי הטבלאות (מהמטמון, 30 יום) ורושם כל קבוצה שה-theme שלה ריק או שווה ל-theme הנוכחי. קבצים ששייכים לקבוצה לא נפלטים בנפרד. אפשר גם לרשום קבוצה ידנית:

PAGE::add_asset_group(3);   // מזהה הקבוצה ב-minify_groups

מה minify::index עושה#

  1. טוען את הקבוצה לפי ה-id שבכתובת.
  2. JS: מדביק את כל הקבצים לפי ord ומעביר ב-JShrink.
  3. CSS: קבצי .scss.css עוברים FILES::scss, שאר הקבצים נטענים ב-include(), וכל url(...) יחסי נכתב מחדש לנתיב מוחלט. התגובה נשלחת עם Expires ו-Cache-Control של שבוע, ועם 304 כשהדפדפן שלח If-Modified-Since חדש מספיק.
  4. אם התוצאה אינה ריקה היא נכתבת ל-cache/<SERVER_NAME>/minify/<id>.<type>ver=<ver>.<type> (קובץ זמני ואז rename, כדי שקורא לא יתפוס קובץ חצי כתוב). תוצאה ריקה לא נשמרת.
הערה

בקוד הליבה לא נמצא קורא של קובץ המטמון הזה בצד PHP (ל-scss/ יש, ב-cache_php_check). לא אימתתי איך קבצי minify/ מוגשים בבקשות חוזרות, כנראה לפי כללי שרת האינטרנט וה-CDN והגרסה שבכתובת.

אזהרה

קבצי CSS בקבוצה נטענים ב-include(), לא ב-file_get_contents: כל <?php שבתוכם יתבצע (CORE-26). רשמו בקבוצות רק קבצים שאתם שולטים בהם, ושמרו על הרשאות כתיבה מצומצמות לתיקיות האתר. כמו כן, קובץ JS חסר בקבוצה מסיים את הבקשה עם not found - <path>, וקבוצה של CSS שנבנית פעמיים באותו תהליך תיכשל על הצהרה כפולה של הפונקציה compress.

סקריפטים חיצוניים וניהול תגיות#

לסקריפטים של צד שלישי (אנליטיקס, פיקסלים, צ'אט) יש שני מנגנונים, ושניהם בלי שינוי קוד:

פאנלטבלהמה זה
Scripts (ADMINMODULE_Scripts)scriptsסקריפט או תג HTML גולמי, עם area (head, body_start, body_end) ו-is_active. נפלט ב-HTML ע"י PAGE::output, ממוין לפי ord, וקיים רק באתר הציבורי. הרשימה במטמון scripts ל-48 שעות, והפאנל מנקה אותה
Tag manager (ADMINMODULE_tag_manager)tag_manager_groups, tag_managerקבוצות של תגים, עם environment (all, dev, production), area, page_key, is_after_done ו-is_active

תגי ה-tag manager לא נכתבים ל-HTML של העמוד. הם מוגשים כ-JS מהכתובת /tag_manager.js?id=<group_id>, שמחזירה applyTags([...]), ו-system.js מזריק כל תג למקום שנבחר (head, body_start או body_end). את תג ה-<script src> של קבוצה מסוימת מוסיפים בעצמכם (למשל כנכס או כסקריפט בפאנל Scripts); הליבה מוסיפה אוטומטית רק את tag_manager.js?id=system, כשקיים הקובץ system/system_tags.json. אפשר לסנן לפי מפתח: ?keys=checkout,cart מחזיר רק תגים עם page_key מתאים, ותג בלי page_key נטען תמיד. תגים עם environment שלא תואם ל-CONFIG::$environment מסוננים בשרת. תג שסומן is_after_done נטען רק אחרי שכל הסקריפטים החיצוניים של אותה קבוצה הסתיימו.

התשובה של tag_manager.js עם no-store, ולכל כתובת מקומית בתוכה (src או href) מתווסף ver. התגים עצמם נשמרים במטמון הפריטים tag_manager.

שימו לב

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

attaches_version: ניקוי מטמונים#

attaches_version היא שורה בטבלת params (קריאה: PARAMS::get('attaches_version')). היא מספר שעולה ב-1 בכל פעולת "ניקוי מטמון" מהאדמין (Tools::delete_cache) ובכל עדכון גרסה של הליבה. הערך משתתף ב:

  • ?ver= בכל נכס ש-PAGE פולט.
  • שם קובץ המטמון של SCSS ושל קבוצות minify.
  • הכתובת tag_manager.js?id=...&ver=....

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

שימו לב

שתי נקודות שלא מכוסות ע"י הגרסה: ייבוא מודולי Vue בפאנל הניהול מוסיף ?ver= עם time() (CORE-44), כך שהפאנל מוריד אותם מחדש בכל טעינה; וקבצי הליבה הסטטיים תחת /assets/ מוגשים דרך PHP עם readfile וללא Cache-Control או Last-Modified (CORE-57), כך שכל קובץ עובר את ה-bootstrap של המערכת.

ראו גם#

מצאתם טעות או חוסר? תקנו את הדף או פתחו Issue בריפו. התיעוד נכתב מתוך הקוד של ליבה 5.0.115.