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

القياس
قبل البدء في إضافة التحسينات، من الأفضل دائمًا تحليل الحالة الحالية للتطبيق أولاً.
- انقر على إنشاء ريمكس للتعديل لجعل المشروع قابلاً للتعديل.
- لمعاينة الموقع الإلكتروني، انقر على عرض التطبيق، ثم انقر على
ملء الشاشة
.
في الدرس التطبيقي السابق حول الترميز بشأن تصغير حمولات البيانات على الشبكة وضغطها،
قلّلنا حجم main.js من 225 كيلوبايت إلى 61.6 كيلوبايت. في هذا الدرس التطبيقي حول الترميز،
ستتعرّف على كيفية تقليل حجم هذه الحزمة بشكل أكبر باستخدام ضغط 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
تعتمد طريقة إعداد خادم ويب لإرسال الموارد المرمّزة باستخدام Brotli على كيفية ترميزها. يمكنك ضغط الموارد ديناميكيًا باستخدام Brotli في وقت الطلب (ضغط ديناميكي)، أو ترميزها مسبقًا بحيث تكون مضغوطة عندما يطلبها المستخدم (ضغط ثابت).
الضغط الديناميكي
يتضمن الضغط الديناميكي ضغط مواد العرض بشكل فوري عندما يطلبها المتصفّح.
المزايا
- لا حاجة إلى إنشاء نُسخ مضغوطة محفوظة من مواد العرض وتعديلها.
- يكون الضغط الفوري مفيدًا بشكل خاص لصفحات الويب التي يتم إنشاؤها ديناميكيًا.
العيوب
- يستغرق ضغط الملفات بمستويات أعلى لتحقيق نِسب ضغط أفضل وقتًا أطول. قد يؤدي ذلك إلى انخفاض في الأداء بينما ينتظر المستخدم ضغط مواد العرض قبل أن يرسلها الخادم.
الضغط الديناميكي باستخدام Node وExpress
ملف server.js مسؤول عن إعداد خادم Node الذي يستضيف التطبيق.
const express = require('express');
const app = express();
app.use(express.static('public'));
const 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 مع كل عملية إنشاء).
للتأكّد من ضغط جميع مواد العرض باستخدام brotli في كل مرة يتم فيها طلبها، يمكن استخدام الوحدة shrink-ray. ابدأ بإضافته كـ devDependency في package.json:
"devDependencies": {
// ...
"shrink-ray": "^0.1.3"
},
واستيرادها إلى ملف الخادم، server.js:
const express = require('express');
const shrinkRay = require('shrink-ray');
وإضافته كبرمجيات وسيطة قبل تحميل express.static:
// ...
const app = express();
// Compress all requests
app.use(shrinkRay());
app.use(express.static('public'));
الآن، أعِد تحميل التطبيق وألقِ نظرة على حجم الحزمة في لوحة "الشبكة":
يمكنك الآن رؤية تطبيق brotli من bz في عنوان Content-Encoding.
تم تقليل حجم main.bundle.js من 225 كيلوبايت إلى 53.1 كيلوبايت. هذا الحجم أصغر بنسبة %14 تقريبًا
مقارنةً بـ gzip (61.6 كيلوبايت).
الضغط الثابت
تعتمد فكرة الضغط الثابت على ضغط مواد العرض وحفظها مسبقًا.
المزايا
- لم يعد وقت الاستجابة بسبب مستويات الضغط العالية مصدر قلق. لا حاجة إلى ضغط الملفات أثناء التنقّل، إذ يمكن الآن جلبها مباشرةً.
العيوب
- يجب ضغط مواد العرض مع كل إصدار. يمكن أن تزيد أوقات الإنشاء بشكل كبير في حال استخدام مستويات ضغط عالية.
الضغط الثابت باستخدام Node وExpress مع webpack
بما أنّ الضغط الثابت يتضمن ضغط الملفات مسبقًا، يمكن تعديل إعدادات webpack لضغط مواد العرض كجزء من خطوة الإنشاء. يمكن استخدام 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: تتم معالجة جميع مواد العرض التي تتطابق مع هذا التعبير العادي (أي مواد عرض JavaScript التي تنتهي بـ.js).
على سبيل المثال، ستتم إعادة تسمية main.js إلى main.js.br.
عند إعادة تحميل التطبيق وإعادة إنشائه، يتم الآن إنشاء نسخة مضغوطة من الحزمة الرئيسية. افتح وحدة تحكّم Glitch لإلقاء نظرة على محتوى الدليل النهائي
public/ الذي يوفّره خادم Node.
- انقر على الزر أدوات.
- انقر على الزر وحدة التحكّم.
- في وحدة التحكّم، نفِّذ الأوامر التالية للانتقال إلى دليل
publicوالاطّلاع على جميع ملفاته:
cd public
ls -lh
تم الآن حفظ نسخة الحزمة المضغوطة باستخدام brotli، وهي main.bundle.js.br،
هنا أيضًا، وهي أصغر حجمًا بنسبة %76 تقريبًا (225 كيلوبايت مقابل 53 كيلوبايت) من
main.bundle.js.
بعد ذلك، اطلب من الخادم إرسال هذه الملفات المضغوطة باستخدام brotli كلما تم طلب إصدارات JS الأصلية. ويمكن إجراء ذلك من خلال تحديد مسار جديد في server.js قبل عرض الملفات باستخدام express.static.
const express = require('express');
const 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:
const express = require('express');
const 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'));
بعد إعادة تحميل التطبيق، ألقِ نظرة على لوحة الشبكة مرة أخرى.
اكتمال النقل بنجاح لقد استخدمت ضغط Brotli لضغط مواد العرض بشكل أكبر!
الخاتمة
يوضّح هذا الدرس التطبيقي حول الترميز كيف يمكن لـ brotli تقليل حجم تطبيقك الإجمالي بشكل أكبر. عند توفّر brotli، تكون خوارزمية ضغط أكثر فعالية من gzip.