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

يستكشف هذا الدرس التطبيقي حول الترميز كيفية تصغير ملف JavaScript وضغطه للتطبيق التالي على تحسين أداء الصفحة من خلال تقليل حجم طلب التطبيق.

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

القياس

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

  • لمعاينة الموقع الإلكتروني، اضغط على عرض التطبيق. ثم اضغط ملء الشاشة ملء الشاشة

هذا التطبيق، والذي يخضع أيضًا للقسم "إزالة غير مستخدم" درس تطبيقي حول الترميز، يسمح لك بالتصويت للأفضل هرّة 🐈

يمكنك الآن إلقاء نظرة على حجم هذا التطبيق:

  1. اضغط على "Control+Shift+J" (أو "Command+Option+J" على أجهزة Mac) لفتح "أدوات مطوري البرامج".
  2. انقر على علامة التبويب الشبكة.
  3. ضع علامة في مربّع الاختيار إيقاف ذاكرة التخزين المؤقت.
  4. أعِد تحميل التطبيق.

حجم الحزمة الأصلي في لوحة "الشبكة"

على الرغم من إحراز الكثير من التقدّم في عملية "إزالة الرمز غير المستخدَم" درس تطبيقي حول الترميز لخفض حجم الحزمة، فما زال حجمها 225 كيلوبايت كبيرًا جدًا.

تصغير

بالنظر إلى مجموعة الرموز التالية.

function soNice() {
  let counter = 0;

  while (counter < 100) {
    console.log('nice');
    counter++;
  }
}

إذا تم حفظ هذه الدالة في ملف خاص بها، فسيكون حجم الملف حوالي 112 بايت (بايت).

في حال إزالة جميع المسافات البيضاء، سيظهر الرمز الناتج على النحو التالي:

function soNice(){let counter=0;while(counter<100){console.log("nice");counter++;}}

أصبح حجم الملف الآن حوالي 83 بايت. وإذا تشوشها بشكل أكبر عن طريق تقليل طول اسم المتغير وتعديل بعض التعبيرات، فقد تكون الكود النهائي ينتهي الأمر بالشكل التالي:

function soNice(){for(let i=0;i<100;)console.log("nice"),i++}

يصل حجم الملف الآن إلى 62 بايت.

مع كل خطوة، يصبح من الصعب قراءة التعليمة البرمجية. ومع ذلك، فإن واجهة برمجة التطبيقات ويفسر محرّك بحث JavaScript كلاً منهما بالطريقة نفسها. تشير رسالة الأشكال البيانية أهمية إخفاء مفاتيح فك التشفير بهذه الطريقة في الوصول إلى ملفات أصغر مختلفة. لم يكن 112 B كثيرًا في البداية، ولكن كان لا يزال هناك انخفاض في الحجم!

في هذا التطبيق، يتم استخدام الإصدار 4 من webpack أداة تجميع الوحدات. يمكن الاطّلاع على الإصدار المحدّد في package.json.

"devDependencies": {
  //...
  "webpack": "^4.16.4",
  //...
}

يعمل الإصدار 4 على تصغير الحزمة تلقائيًا أثناء وضع الإنتاج. تستخدم TerserWebpackPlugin مكوّن إضافي لـ Terser. وTerser هي أداة شائعة تُستخدم لضغط رمز JavaScript.

للحصول على فكرة حول شكل الرمز المصغر، يمكنك المتابعة والنقر main.bundle.js أثناء تواجدك في لوحة الشبكة في "أدوات مطوري البرامج". الآن، انقر على زر علامة التبويب الردّ.

ردّ مصغّر

ويظهر الرمز في شكله النهائي، بشكل مصغّر ومشوّه، في نص الاستجابة. لمعرفة حجم الحزمة إذا لم يتم تصغيرها، افتح webpack.config.js وعدِّل إعدادات mode.

module.exports = {
  mode: 'production',
  mode: 'none',
  //...

أعِد تحميل التطبيق والاطّلاع على حجم الحزمة من جديد من خلال لوحة الشبكة في أدوات مطوّري البرامج

حجم الحزمة 767 كيلوبايت

وهذا فرق كبير جدًا! 😅

تأكَّد من التراجع عن التغييرات هنا قبل المتابعة.

module.exports = {
  mode: 'production',
  mode: 'none',
  //...

إنّ تضمين عملية لتصغير الرمز في تطبيقك يعتمد على الأدوات. المستخدم:

  • في حال استخدام الإصدار 4 من حزمة الويب أو الإصدارات الأحدث، ليس عليك اتّخاذ أي إجراءات إضافية. حيث يتم تصغير الرمز بشكل افتراضي في وضع الإنتاج. 👍
  • في حال استخدام إصدار قديم من حزمة الويب، عليك تثبيت TerserWebpackPlugin وتضمينه. في عملية إنشاء حزمة الويب. المستندات يشرح هذا بالتفصيل.
  • توجد أيضًا مكونات إضافية أخرى للتصغير ويمكن استخدامها بدلاً من ذلك، مثل BabelMinifyWebpackPlugin. وClosureCompilerPlugin.
  • في حال عدم استخدام أحد برامج تجميع الوحدات على الإطلاق، استخدِم Terser كأداة واجهة سطر الأوامر أو تضمينها بشكل مباشر كتبعية.

الضغط

وعلى الرغم من أن مصطلح "الضغط" بشكل غير دقيق أحيانًا لشرح كيف يتم خلال عملية التصغير، فإنه لا يتم ضغطه بالفعل في والمعنى الحرفي.

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

مع كل طلب واستجابة HTTP، يمكن أن تضيف المتصفحات وخوادم الويب الرؤوس المراد تضمينها إضافية حول مادة العرض التي يتم جلبها أو استلامها. يمكن أن تظهر فائدة هذا بوضوح أكبر يظهر في علامة التبويب Headers ضمن لوحة شبكة أدوات مطوّري البرامج حيث تظهر ثلاثة أنواع تظهر:

  • تمثل القيمة العامة العناوين العامة ذات الصلة بالطلب-الاستجابة بالكامل التفاعل.
  • تعرض عناوين الاستجابة قائمة بالعناوين الخاصة بالاستجابة الفعلية. من الخادم.
  • تعرض عناوين الطلب قائمة بالعناوين المرفقة بالطلب من خلال البرنامج.

يمكنك إلقاء نظرة على عنوان accept-encoding في Request Headers.

قبول عنوان الترميز

يستخدم المتصفح accept-encoding لتحديد المحتوى. أو تنسيقات التشفير، أو خوارزميات الضغط، فإنه يدعمها. هناك العديد من هناك خوارزميات لضغط النص، ولكن هناك ثلاثة منها فقط هنا لضغط (وفك ضغط) طلبات شبكة HTTP:

  • Gzip (gzip): الضغط الأكثر استخدامًا لتفاعلات الخادم والعميل. تعتمد على الانكماش في جميع المتصفحات الحالية.
  • تكبير/تصغير (deflate): لا يُستخدَم عادةً.
  • Brotli (br): ضغط جديد إلى تحسين نِسَب الضغط بشكل أكبر، ما قد ينتج عنه تحميل الصفحات بشكل أسرع. إنها متاحة في أحدث إصدارات من معظم المتصفحات.

يتطابق نموذج التطبيق في هذا البرنامج التعليمي مع التطبيق المكتمل في درس تطبيقي حول الترميز "Remove unused code" (إزالة الرمز غير المستخدَم) باستثناء حقيقة يتم استخدام Express الآن كإطار عمل للخادم. في المرحلة التالية ،فسيتم استكشاف كل من الضغط الثابت والديناميكي.

الضغط الديناميكي

يتضمن الضغط الديناميكي ضغط الأصول بشكل فوري عند طلبها المتصفح.

الإيجابيات

  • لا حاجة إلى إنشاء نُسخ مضغوطة محفوظة من مواد العرض وتعديلها تم.
  • فضغط المحتوى بشكل فوري يناسب صفحات الويب التي يتم إنشاؤه ديناميكيًا.

السلبيات

  • ضغط الملفات على مستويات أعلى لتحقيق نسب ضغط أفضل تستغرق وقتًا أطول. ويمكن أن يؤدّي ذلك إلى نتيجة أداء بينما ينتظر المستخدِم وصول مواد العرض إلى قبل أن يتم إرسالها بواسطة الخادم.

الضغط الديناميكي باستخدام Node/Express

الملف server.js مسؤول عن إعداد خادم العُقدة الذي يستضيفه التطبيق.

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

للتأكد من ضغط جميع مواد العرض في كل مرة يتم فيها طلبها، للبرمجيات الوسيطة compression تنبؤي. ابدأ بإضافته كـ devDependency في package.json:

"devDependencies": {
  //...
  "compression": "^1.7.3"
},

واستيراده إلى ملف الخادم، server.js:

const express = require('express');
const compression = require('compression');

ويمكنك إضافتها كبرمجية وسيطة قبل تثبيت express.static:

//...

const app = express();

app.use(compression());

app.use(express.static('public'));

//...

يمكنك الآن إعادة تحميل التطبيق وإلقاء نظرة على حجم الحزمة في لوحة الشبكة.

حجم الحزمة مع الضغط الديناميكي

من 225 كيلوبايت إلى 61.6 كيلوبايت! في Response Headers الآن، هناك content-encoding أن الخادم يرسل هذا الملف بترميز gzip.

عنوان ترميز المحتوى

الضغط الثابت

إنّ الهدف من الضغط الثابت هو ضغط مواد العرض وحفظها. مسبقًا.

الإيجابيات

  • لم يعُد وقت الاستجابة بسبب مستويات الضغط العالية مصدر قلق. لا يلزم اتخاذ أي إجراء بشكل فوري لضغط الملفات حيث يمكن جلبها مباشرة الآن.

السلبيات

  • يجب ضغط مواد العرض مع كل إصدار. قد تزداد أوقات البناء بشكل كبير إذا تم استخدام مستويات ضغط عالية.

ضغط ثابت مع Node/Express وwebpack

نظرًا لأن الضغط الثابت يتضمن ضغط الملفات مسبقًا، فإن webpack يمكن تعديل الإعدادات لضغط مواد العرض كجزء من خطوة التصميم. CompressionPlugin استخدامها لذلك.

ابدأ بإضافته كـ devDependency في package.json:

"devDependencies": {
  //...
  "compression-webpack-plugin": "^1.1.11"
},

مثل أي مكون إضافي آخر لحزمة الويب، قم باستيراده في ملف التهيئات، webpack.config.js:

const path = require("path");

//...

const CompressionPlugin = require("compression-webpack-plugin");

وأدرِجها في مصفوفة plugins:

module.exports = {
  //...
  plugins: [
    //...
    new CompressionPlugin()
  ]
}

يضغط المكوّن الإضافي تلقائيًا ملفات الإصدار باستخدام gzip. إلقاء نظرة ضمن المستندات لمعرفة كيفية إضافة خيارات لاستخدام خوارزمية مختلفة أو تضمين/استبعاد ملفات معينة.

وعند إعادة تحميل التطبيق وإعادة إنشائه، يتم إنشاء نسخة مضغوطة من الحزمة الرئيسية تم إنشاؤها الآن. افتح Glitch Console لإلقاء نظرة على المحتوى. دليل public/ الأخير الذي يعرضه خادم Node.

  • انقر على زر الأدوات.
  • انقر على الزر وحدة التحكّم.
  • في وحدة التحكّم، شغِّل الأوامر التالية لتغييرها إلى public. الدليل وعرض كل ملفاته:
cd public
ls

الملفات النهائية التي يتم إخراجها في الدليل العام

الإصدار المضغوط من الحزمة main.bundle.js.gz هو الإصدار المضغوط بتنسيق gzip، وهو محفوظ الآن هنا باسم أيضًا. تضغط CompressionPlugin أيضًا index.html تلقائيًا.

الشيء التالي الذي يجب القيام به هو إخبار الخادم بإرسال ملفات مضغوطة بتنسيق gzip الملفات عند طلب نُسخ JavaScript الأصلية الخاصة بها. يمكن إجراء ذلك من خلال تحديد مسار جديد في server.js قبل عرض الملفات باستخدام express.static

const express = require('express');
const app = express();

app.get('*.js', (req, res, next) => {
  req.url = req.url + '.gz';
  res.set('Content-Encoding', 'gzip');
  next();
});

app.use(express.static('public'));

//...

تُستخدم app.get لإخبار الخادم بكيفية الاستجابة لطلب GET الخاص بـ بنقطة نهاية محددة. يتم بعد ذلك استخدام دالة الاستدعاء لتحديد كيفية التعامل مع هذا طلبك. ويعمل المسار على النحو التالي:

  • يعني تحديد '*.js' كوسيطة أولى أنّ هذه المعلَمة متوافقة مع كل نقطة نهاية يتم تنشيطها لجلب ملف JS.
  • ضمن معاودة الاتصال، يتم إرفاق .gz بعنوان URL للطلب تم ضبط عنوان الاستجابة Content-Encoding على gzip.
  • وأخيرًا، تضمن next() استمرار التسلسل مع أي معاودة اتصال. قد يكون التالي.

بعد إعادة تحميل التطبيق، ألقِ نظرة على لوحة Network مرة أخرى.

تقليل حجم الحزمة باستخدام الضغط الثابت

وكما حدث من قبل، حدث انخفاض كبير في حجم الحزمة.

الخاتمة

تناول هذا الدرس التطبيقي حول الترميز عملية تصغير وضغط رمز المصدر. أصبحت هاتان الوسيلتان طريقة افتراضية في العديد من الأدوات متاحة اليوم، لذا من المهم معرفة ما إذا كانت سلسلة الأدوات لديك يدعمهما أو إذا كان ينبغي عليك البدء في تطبيق كلتا العمليتين بنفسك.