הקטנה ודחיסה של מטענים ייעודיים (payload) ברשת באמצעות brotli

מייקל דיבלסיו
מייקל דיבלאסיו

ה-codelab הוא תוסף ל-Minify and compression paylab (הקטנה ודחיסה של מטענים ייעודיים) ברשת – בהנחה שאתם מכירים את המושגים הבסיסיים של הדחיסה. בהשוואה לאלגוריתמים אחרים של דחיסה כמו gzip, ב-Codelab הזה תלמדו איך דחיסת Brotli יכולה לצמצם עוד יותר את יחסי הדחיסה ואת הגודל הכולל של האפליקציה.

צילום מסך של האפליקציה

מדידה

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

  1. לוחצים על רמיקס לעריכה כדי שיהיה אפשר לערוך את הפרויקט.
  2. כדי לראות תצוגה מקדימה של האתר, מקישים על View App ואז על Fullscreen מסך מלא.

בשיעור הקודם של Minify and דחיסת נתוני מטענים ייעודיים (paylab), הורדנו את הגודל של main.js מ-225KB ל-61.6KB. בשיעור הזה תלמדו איך דחיסת Brotli יכולה לעזור לכם לצמצם את גודל החבילה עוד יותר.

דחיסת ברוטלי

Brotli הוא אלגוריתם חדש יותר לדחיסת טקסט שיכול לספק תוצאות טובות יותר לדחיסת טקסט בהשוואה ל-gzip. על פי CertSimple, הביצועים של Brotli הם:

  • קטן ב-14% מ-gzip עבור JavaScript
  • קטן ב-21% מ-gzip ל-HTML
  • 17% פחות מ-gzip בשירות CSS

כדי להשתמש ב-Brotli, השרת חייב לתמוך ב-HTTPS. Brotli נתמך בגרסאות האחרונות של רוב הדפדפנים. דפדפנים שתומכים ב-Brotli יכללו את br בכותרות Accept-Encoding:

Accept-Encoding: gzip, deflate, br

אפשר לקבוע באיזה אלגוריתם דחיסה נעשה שימוש בשדה Content-Encoding בכרטיסייה 'רשת הכלים למפתחים ב-Chrome' (Command+Option+I או Ctrl+Alt+I):

חלונית 'רשת'

הפעלה של Brotli

דחיסה דינמית

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

יתרונות

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

חסרונות

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

דחיסה דינמית עם צומת/אקספרס

הקובץ server.js אחראי להגדרת שרת הצמתים שמארח את האפליקציה.

var express = require('express');

var app = express();

app.use(express.static('public'));

var listener = app.listen(process.env.PORT, function() {
  console.log('Your app is listening on port ' + listener.address().port);
});

בשלב הזה, כל מה שצריך לעשות הוא לייבא את express ולהשתמש בתווכה express.static כדי לטעון את כל קובצי ה-HTML, ה-JS וה-CSS הסטטיים ב-public/directory (והקבצים האלה נוצרים על ידי Webpack עם כל build).

כדי לוודא שכל הנכסים דחוסים באמצעות brotli בכל פעם שמבקשים אותם, אפשר להשתמש במודול shrink-ray. כדי להתחיל, יש להוסיף אותו כ-devDependency ב-package.json:

"devDependencies": {
  //...
  "shrink-ray": "^0.1.3"
},

ומייבאים אותו לקובץ השרת, server.js:

var express = require('express');
var shrinkRay = require('shrink-ray');

ויש להוסיף אותו כתווכה לפני טעינת express.static:

//...
var app = express();

// compress all requests
app.use(shrinkRay());

app.use(express.static('public'));

עכשיו צריך לטעון מחדש את האפליקציה ולבדוק את גודל החבילה בחלונית 'רשת':

גודל חבילה עם דחיסת Brotli דינמית

עכשיו אפשר לראות שההחלה של brotli מתבצעת מ-bz בכותרת הContent-Encoding. main.bundle.js ירד מ-225KB ל-53.1KB! זה קטן ב-14% בערך בהשוואה ל-gzip (61.6KB).

דחיסה סטטית

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

יתרונות

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

חסרונות

  • צריך לדחוס את הנכסים בכל build. זמני ה-build יכולים להתארך משמעותית אם משתמשים ברמות דחיסה גבוהות.

דחיסה סטטית עם Node/Express ו-webpack

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

כדי להתחיל, יש להוסיף אותו כ-devDependency ב-package.json:

"devDependencies": {
  //...
 "brotli-webpack-plugin": "^1.1.0"
},

כמו כל פלאגין אחר של Webpack, מייבאים אותו בקובץ ההגדרות, webpack.config.js:

var path = require("path");

//...
var BrotliPlugin = require('brotli-webpack-plugin');

וכלול אותה במערך יישומי הפלאגין:

module.exports = {
  // ...
  plugins: [
    // ...
    new BrotliPlugin({
      asset: '[file].br',
      test: /\.(js)$/
    })
  ]
},

מערך הפלאגין משתמש בארגומנטים הבאים:

  • asset: השם של נכס היעד.
  • [file] מוחלף בשם הקובץ המקורי של הנכס.
  • test: המערכת מעבדת את כל הנכסים שתואמים ל-RegExp הזה (כלומר, נכסי JavaScript שמסתיימים ב-.js).

לדוגמה, השם של main.js ישתנה ל-main.js.br.

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

  1. לוחצים על הלחצן כלים.
  2. לוחצים על הלחצן מסוף.
  3. במסוף, מריצים את הפקודות הבאות כדי לעבור לספרייה public ולראות את כל הקבצים שבה:
cd public
ls -lh
גודל חבילה עם דחיסת Brotli סטטית

גרסת ה-Brotli הדחוסה של החבילה, main.bundle.js.br, גם שמורה כאן, והיא קטנה ב-76% בערך (225KB לעומת 53KB) לעומת main.bundle.js.

בשלב הבא, מבקשים מהשרת לשלוח את הקבצים האלה דחוסים ב-brotli בכל פעם שמבקשים גרסאות ה-JS המקוריות שלהם. כדי לעשות זאת, אפשר להגדיר מסלול חדש ב-server.js לפני שהקבצים יוצגו באמצעות express.static.

var express = require('express');

var app = express();

app.get('*.js', (req, res, next) => {
  req.url = req.url + '.br';
  res.set('Content-Encoding', 'br');
  res.set('Content-Type', 'application/javascript; charset=UTF-8');
  next();
});

app.use(express.static('public'));

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

  • אם מציינים את '*.js' כארגומנט הראשון, הפעולה הזו פועלת בכל נקודת קצה שמופעלת כדי לאחזר קובץ JS.
  • בקריאה החוזרת, .br מצורפת לכתובת ה-URL של הבקשה וכותרת התגובה Content-Encoding מוגדרת ל-br.
  • הכותרת Content-Type מוגדרת ל-application/javascript; charset=UTF-8 כדי לציין את סוג ה-MIME.
  • לבסוף, next() מבטיח שהרצף ימשיך לכל קריאה חוזרת (callback) שעשויה להיות הבאה.

יכול להיות שחלק מהדפדפנים לא תומכים בדחיסת brotli, לכן חשוב לוודא שיש תמיכה ב-brotli לפני החזרת הקובץ הדחוס ב-brotli. לשם כך, צריך לבדוק שהכותרת של הבקשה Accept-Encoding כוללת את הערך br:

var express = require('express');

var app = express();

app.get('*.js', (req, res, next) => {
  if (req.header('Accept-Encoding').includes('br')) {
    req.url = req.url + '.br';
    console.log(req.header('Accept-Encoding'));
    res.set('Content-Encoding', 'br');
    res.set('Content-Type', 'application/javascript; charset=UTF-8');
  }
  next();
});

app.use(express.static('public'));

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

גודל החבילה 53.1KB (מ-225KB)

זהו! השתמשת בדחיסת Brotli כדי להמשיך לדחוס את הנכסים שלך!

סיכום

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