هذا الدرس التطبيقي حول الترميز هو امتداد للدرس التطبيقي حول الترميز الخاص بتصغير حجم حمولات الشبكة وضغطها،
ويفترض أنّك على دراية بالمفاهيم الأساسية للضغط. مقارنةً بخوارزميات الضغط الأخرى، مثل gzip
، يستكشف هذا الدرس العملي كيفية تقليل نسب الضغط وحجم تطبيقك الإجمالي بشكل أكبر باستخدام ضغط Brotli (br
).
القياس
قبل البدء في إضافة تحسينات، من الأفضل دائمًا تحليل الحالة الحالية للتطبيق.
- انقر على إنشاء ريمكس للتعديل لجعل المشروع قابلاً للتعديل.
- لمعاينة الموقع الإلكتروني، انقر على عرض التطبيق، ثم انقر على
ملء الشاشة
.
في درس Minify and compress network payloads
البرمجي السابق،
قلّلنا حجم 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 Console للاطّلاع على محتوى الدليل النهائي
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
.