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

Michael DiBlasio
Michael DiBlasio

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

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

מדידה

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

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

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

דחיסה של 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

דחיסה דינמית

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

יתרונות

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

חסרונות

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

דחיסה דינמית באמצעות Node/Express

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

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/ הסופית שמוצגת על ידי שרת ה-Node.

  1. לוחצים על הלחצן כלים.
  2. לוחצים על הלחצן Console.
  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.