تصغير حمولات الشبكة وضغطها باستخدام brotli

هذا الدرس التطبيقي حول الترميز هو إضافة إلى الدرس التطبيقي حول ترميز حمولات الشبكة وضغطها ويفترض أنّك على دراية بمفاهيم الضغط الأساسية. مقارنةً بخوارزميات الضغط الأخرى مثل gzip، يستكشف هذا الدرس التطبيقي حول الترميز كيف يمكن لضغط Brotli تقليل نسب الضغط والحجم الإجمالي لتطبيقك.

لقطة شاشة التطبيق

قياس

قبل الشروع في إضافة تحسينات، من الجيد دائمًا تحليل الحالة الراهنة للتطبيق.

  1. انقر على إنشاء ريمكس لتعديل لجعل المشروع قابلاً للتعديل.
  2. لمعاينة الموقع الإلكتروني، اضغط على عرض التطبيق، ثم اضغط على ملء الشاشة ملء الشاشة.

في الدرس التطبيقي السابق حول ترميز حمولات الشبكة وضغطها، خفّضنا حجم main.js من 225 كيلوبايت إلى 61.6 كيلوبايت. في هذا الدرس التطبيقي حول الترميز، ستكتشف كيف يمكن لضغط Brotli تقليل حجم الحزمة بشكل أكبر.

ضغط Brotli

Brotli هي خوارزمية ضغط أحدث يمكنها تقديم نتائج ضغط أفضل من gzip. وفقًا لـ CertSimple، فإن أداء Brotli هو:

  • أصغر من gzip بنسبة 14% للغة JavaScript
  • أصغر بنسبة 21% من gzip لـ HTML
  • أصغر بنسبة% 17 من gzip لخدمة مقارنة الأسعار

لاستخدام 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 مسؤولاً عن إعداد خادم العُقدة الذي يستضيف التطبيق.

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 (ويتم إنشاء هذه الملفات بواسطة حزمة الويب مع كل إصدار).

للتأكّد من ضغط جميع مواد العرض باستخدام 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 من 225 كيلوبايت إلى 53.1 كيلوبايت! وهو أصغر بنسبة 14% تقريبًا مقارنةً بـ gzip (61.6 كيلوبايت).

الضغط الثابت

تكمن الفكرة وراء الضغط الثابت في ضغط الأصول وحفظها مسبقًا.

الإيجابيات

  • لم يعُد وقت الاستجابة بسبب مستويات الضغط العالية مصدر قلق. ولا يلزم حدوث أي شيء بشكل فوري لضغط الملفات حيث يمكن الآن جلبها مباشرةً.

السلبيات

  • يجب ضغط مواد العرض مع كل إصدار. يمكن أن تزداد مدة الإنشاء بشكل كبير في حالة استخدام مستويات ضغط عالية.

ضغط ثابت مع Node/Express وwebpack

بما أنّ الضغط الثابت يتضمّن ضغط الملفات في وقت مبكر، يمكن تعديل إعدادات حزمة الويب لضغط مواد العرض كجزء من خطوة الإنشاء. ويمكن استخدام brotli-webpack-plugin لذلك.

ابدأ بإضافته كـ devDependency في package.json:

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

مثل أي مكوّن إضافي آخر لحزمة الويب، عليك استيراده في ملف الإعدادات، 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: تتم معالجة جميع مواد العرض التي تتطابق مع هذا التعبير العادي (أي مواد عرض JavaScript التي تنتهي بـ .js).

على سبيل المثال، ستتم إعادة تسمية main.js إلى main.js.br.

وعند إعادة تحميل التطبيق وإعادة إنشائه، يتم الآن إنشاء نسخة مضغوطة من الحزمة الرئيسية. افتح Glitch Console لإلقاء نظرة على محتوى الدليل public/ الأخير الذي يعرضه خادم Node.

  1. انقر على زر الأدوات.
  2. انقر على الزر وحدة التحكّم.
  3. في وحدة التحكّم، شغِّل الأوامر التالية للتغيير إلى دليل public والاطّلاع على كل ملفاته:
cd public
ls -lh
حجم الحزمة مع ضغط Brotli الثابت

تم حفظ نسخة brotli المضغوطة من الحزمة، main.bundle.js.br، هنا أيضًا، كما أنها أصغر حجمًا بنسبة 76% (225 كيلوبايت مقابل 53 كيلوبايت) من 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() استمرار التسلسل مع أي استدعاء قد يكون بعد ذلك.

بما أنّ بعض المتصفّحات قد لا تتوافق مع ضغط 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.1 كيلوبايت (من 225 كيلوبايت)

اكتمال عملية النقل بنجاح لقد استخدمت ضغط Brotli لضغط مواد العرض بشكل أكبر.

الخلاصة

يوضّح هذا الدرس التطبيقي حول الترميز كيف يمكن لتطبيق brotli تقليل الحجم الإجمالي لتطبيقك. تُعد brotli خوارزمية ضغط أكثر فعالية من gzip في حال توفّرها.