هذا الدرس التطبيقي حول الترميز هو امتداد لدرس تصغير حِزم بيانات الشبكة وضغطها
التطبيقي، ويفترض أنّك على دراية بأساسيات ضغط البيانات. مقارنةً بالخوارزميات الأخرى للضغط، مثل 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 و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
: تتم معالجة جميع مواد العرض التي تتطابق مع تعبير RegExp هذا (أي مواد عرض JavaScript التي تنتهي بعلامة.js
).
على سبيل المثال، ستتم إعادة تسمية main.js
إلى main.js.br
.
عند إعادة تحميل التطبيق وإعادة إنشائه، يتم الآن إنشاء نسخة مضغوطة من الحزمة الرئيسية. افتح Glitch Console للاطّلاع على المحتوى المتوفّر في الدليل النهائي
public/
الذي يعرضه خادم Node.
- انقر على زر الأدوات.
- انقر على الزر وحدة التحكّم.
- في وحدة التحكّم، نفِّذ الأوامر التالية للانتقال إلى الدليل
public
والاطّلاع على جميع ملفاته:
cd public
ls -lh
تم حفظ الإصدار المضغوط من الحزمة main.bundle.js.br
باستخدام تقنية brotli
هنا أيضًا، وهو أصغر حجمًا بنسبة% 76 تقريبًا (225 كيلوبايت مقارنةً بـ 53 كيلوبايت) مقارنةً بملف
main.bundle.js
.
بعد ذلك، عليك إرسال تعليمات إلى الخادم لإرسال هذه الملفات المضغوطة بتنسيق brotli عند طلب
إصدارات JavaScript الأصلية. ويمكن إجراء ذلك من خلال تحديد مسار جديد في 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
، وذلك في الحالات التي تتوفّر فيها.