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

مايكل ديبلاسيو
مايكل ديبلاسيو

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

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

قياس

قبل البدء بإضافة تحسينات، من الأفضل دائمًا تحليل الحالة الحالية للتطبيق أولاً.

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

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

ضغط بروتلي

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

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

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

وللتأكّد من ضغط جميع مواد العرض باستخدام botli في كل مرة يتم طلبها، يمكن استخدام وحدة 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/ الأخير الذي يعرضه خادم العقدة.

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

تم الآن حفظ نسخة الحزمة المضغوطة من botli، main.bundle.js.br، هنا أيضًا، وهي حجمها أصغر بنسبة% 76 (225 كيلوبايت مقابل 53 كيلوبايت) من main.bundle.js.

بعد ذلك، اطلُب من الخادم إرسال هذه الملفات المضغوطة بتنسيق broli كلما تم طلب إصدارات JavaScript الأصلية. يمكن إجراء ذلك من خلال تحديد مسار جديد في 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' كوسيطة أولى أنّ هذه الطريقة مناسبة لكل نقطة نهاية يتم تنشيطها لجلب ملف JavaScript.
  • ضمن معاودة الاتصال، يتم إرفاق .br بعنوان URL للطلب، ويتم ضبط عنوان الاستجابة Content-Encoding على br.
  • تم ضبط عنوان Content-Type على application/javascript; charset=UTF-8 لتحديد نوع MIME.
  • وأخيرًا، تضمن السمة next() استمرار التسلسل في أي استدعاء قد يكون بعد ذلك.

ولأنّ بعض المتصفحات قد لا تتيح ضغط broli، عليك التأكّد من أنّ botli متوافق قبل عرض ملف broli المضغوط، وذلك من خلال التأكُّد من أنّ عنوان الطلب 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، في حال توفّرها.