هذا الدرس التطبيقي حول الترميز هو إضافة إلى الدرس التطبيقي حول ترميز حمولات الشبكة
وضغطها ويفترض أنّك على دراية بمفاهيم الضغط الأساسية. مقارنةً بالخوارزميات الأخرى للضغط، مثل gzip
، يستكشف هذا الدليل التعليمي كيفية استخدام ضغط Brotli (br
) للحدّ بشكل أكبر من نسب الضغط وحجم تطبيقك بشكل عام.
القياس
قبل التعمق في إضافة التحسينات، يُفضل دائمًا تحليل الحالة الحالية للتطبيق أولاً.
- انقر على Remix to Edit (إنشاء ريمكس لتعديله) ليصبح المشروع قابلاً للتعديل.
- لمعاينة الموقع الإلكتروني، اضغط على عرض التطبيق. ثم اضغط على ملء الشاشة .
في الدورة التدريبية السابقة حول تصغير حمولات البيانات على الشبكة وضغطها
،
تم تقليل حجم 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 وJavaScript و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
: تتم معالجة جميع مواد العرض التي تتطابق مع تعبير RegExp هذا (أي مواد عرض 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 من خلال التحقّق مما إذا كان عنوان طلب العميل 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
في الحالات التي تتوفّر فيها.