کوچک سازی و فشرده سازی بارهای شبکه با gzip

این کد لبه بررسی می کند که چگونه کوچک کردن و فشرده سازی بسته جاوا اسکریپت برای برنامه زیر عملکرد صفحه را با کاهش اندازه درخواست برنامه بهبود می بخشد.

اسکرین شات برنامه

اندازه گرفتن

قبل از وارد شدن برای افزودن بهینه سازی، همیشه ایده خوبی است که ابتدا وضعیت فعلی برنامه را تجزیه و تحلیل کنید.

  • برای پیش نمایش سایت، View App را فشار دهید. سپس تمام صفحه را فشار دهید تمام صفحه .

این برنامه، که در لبه کد "حذف کد استفاده نشده" نیز پوشش داده شده است، به شما امکان می دهد به بچه گربه مورد علاقه خود رای دهید. 🐈

حالا نگاهی به حجم این برنامه بیندازید:

  1. «Control+Shift+J» (یا «Command+Option+J» در Mac) را فشار دهید تا DevTools باز شود.
  2. روی تب Network کلیک کنید.
  3. چک باکس Disable cache را انتخاب کنید.
  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 B است. اگر با کاهش طول نام متغیر و اصلاح برخی از عبارات بیشتر مخدوش شود، کد نهایی ممکن است به این شکل باشد:

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

حجم فایل در حال حاضر به 62 بایت رسیده است.

با هر مرحله، خواندن کد سخت تر می شود. با این حال، موتور جاوا اسکریپت مرورگر هر یک از این موارد را دقیقاً به یک روش تفسیر می کند. مزیت مبهم سازی کد به این روش می تواند به دستیابی به اندازه فایل های کوچکتر کمک کند. 112 B واقعاً برای شروع چیز زیادی نبود، اما هنوز 50٪ کاهش در اندازه وجود داشت!

در این اپلیکیشن از وب پک نسخه 4 به عنوان باندلر ماژول استفاده شده است. نسخه خاص را می توان در package.json مشاهده کرد.

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

نسخه 4 در حال حاضر بسته نرم افزاری را به طور پیش فرض در حالت تولید کوچک می کند. از پلاگین TerserWebpackPlugin برای Terser استفاده می کند. Terser ابزار محبوبی است که برای فشرده سازی کد جاوا اسکریپت استفاده می شود.

برای دریافت ایده از شکل کد کوچک شده، ادامه دهید و در حالی که هنوز در پانل DevTools Network هستید، روی main.bundle.js کلیک کنید. حالا روی تب Response کلیک کنید.

پاسخ کوچک شده

کد به شکل نهایی، کوچک شده و مخدوش شده، در بدنه پاسخ نشان داده شده است. برای اینکه بفهمید در صورت کوچک نشدن بسته نرم افزاری چقدر بزرگ بود، webpack.config.js باز کنید و پیکربندی mode را به روز کنید.

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

برنامه را دوباره بارگیری کنید و دوباره از طریق پنل DevTools Network به اندازه بسته نگاهی بیندازید

حجم بسته 767 کیلوبایت

این یک تفاوت بسیار بزرگ است! 😅

قبل از ادامه، حتماً تغییرات را در اینجا برگردانید.

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

گنجاندن فرآیندی برای کوچک کردن کد در برنامه شما بستگی به ابزارهایی دارد که استفاده می کنید:

  • اگر از webpack نسخه 4 یا بالاتر استفاده می شود، نیازی به انجام کار اضافی نیست زیرا کد به طور پیش فرض در حالت تولید کوچک می شود. 👍
  • اگر از نسخه قدیمی‌تر وب‌پک استفاده می‌شود، TerserWebpackPlugin را نصب کرده و در فرآیند ساخت وب‌پک قرار دهید. مستندات این موضوع را به تفصیل توضیح می دهد.
  • افزونه‌های کوچک‌سازی دیگری نیز وجود دارند و می‌توان به جای آن از آنها استفاده کرد، مانند BabelMinifyWebpackPlugin و ClosureCompilerPlugin .
  • اگر یک بسته‌کننده ماژول اصلاً استفاده نمی‌شود، از Terser به عنوان ابزار CLI استفاده کنید یا آن را مستقیماً به عنوان یک وابستگی اضافه کنید.

فشرده سازی

اگرچه گاهی اوقات اصطلاح "فشرده سازی" برای توضیح چگونگی کاهش کد در طی فرآیند کوچک سازی استفاده می شود، اما در واقع به معنای واقعی کلمه فشرده نمی شود.

فشرده سازی معمولاً به کدهایی اطلاق می شود که با استفاده از الگوریتم فشرده سازی داده ها اصلاح شده اند. برخلاف minification که به ارائه کد کاملا معتبر ختم می شود، کد فشرده قبل از استفاده باید از حالت فشرده خارج شود.

با هر درخواست و پاسخ HTTP، مرورگرها و سرورهای وب می‌توانند سرصفحه‌هایی را اضافه کنند تا اطلاعات بیشتری در مورد دارایی‌ای که واکشی یا دریافت می‌شود را شامل شود. این را می توان در تب Headers در پانل DevTools Network مشاهده کرد که در آن سه نوع نشان داده شده است:

  • General نشان دهنده سرصفحه های کلی مربوط به کل تعامل درخواست-پاسخ است.
  • سرصفحه های پاسخ لیستی از هدرهای مخصوص پاسخ واقعی سرور را نشان می دهد.
  • Request Headers فهرستی از هدرهای پیوست شده به درخواست مشتری را نشان می دهد.

به هدر accept-encoding در Request Headers نگاهی بیندازید.

هدر رمزگذاری را بپذیرید

accept-encoding توسط مرورگر برای تعیین فرمت‌های کدگذاری محتوا یا الگوریتم‌های فشرده‌سازی مورد استفاده قرار می‌گیرد. الگوریتم‌های فشرده‌سازی متن زیادی وجود دارد، اما تنها سه الگوریتم برای فشرده‌سازی (و فشرده‌سازی) درخواست‌های شبکه HTTP پشتیبانی می‌شوند:

  • Gzip ( gzip ): پرکاربردترین فرمت فشرده سازی برای تعاملات سرور و مشتری. این بر روی الگوریتم Deflate ساخته شده است و در تمام مرورگرهای فعلی پشتیبانی می شود.
  • باد کردن ( deflate ): معمولا استفاده نمی شود.
  • Brotli ( br ): یک الگوریتم فشرده‌سازی جدیدتر که هدف آن بهبود بیشتر نسبت‌های فشرده‌سازی است که می‌تواند منجر به بارگیری سریع‌تر صفحه شود. در آخرین نسخه های اکثر مرورگرها پشتیبانی می شود.

برنامه نمونه در این آموزش مشابه برنامه تکمیل شده در لبه کد "حذف کد استفاده نشده" است به جز این واقعیت که Express اکنون به عنوان یک چارچوب سرور استفاده می شود. در چند بخش بعدی، فشرده سازی استاتیک و پویا بررسی می شود.

فشرده سازی دینامیک

فشرده‌سازی پویا شامل فشرده‌سازی دارایی‌ها در لحظه به محض درخواست مرورگر است.

طرفداران

  • ایجاد و به‌روزرسانی نسخه‌های فشرده ذخیره‌شده دارایی‌ها نیازی به انجام ندارد.
  • فشرده سازی در حین پرواز به ویژه برای صفحات وب که به صورت پویا تولید می شوند خوب عمل می کند.

منفی

  • فشرده سازی فایل ها در سطوح بالاتر برای دستیابی به نسبت فشرده سازی بهتر زمان بیشتری می برد. این می تواند باعث ضربه عملکرد شود زیرا کاربر منتظر می ماند تا دارایی ها قبل از ارسال توسط سرور فشرده شوند.

فشرده سازی پویا با 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/ دایرکتوری است (و این فایل‌ها توسط بسته وب با هر ساختنی ایجاد می‌شوند).

برای اطمینان از اینکه همه دارایی ها هر بار که درخواست می شوند فشرده می شوند، می توان از کتابخانه میان افزار فشرده سازی استفاده کرد. با افزودن آن به عنوان 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'));

//...

اکنون برنامه را دوباره بارگیری کنید و به اندازه بسته نرم افزاری در پنل Network نگاهی بیندازید.

اندازه بسته نرم افزاری با فشرده سازی پویا

از 225 کیلوبایت تا 61.6 کیلوبایت! اکنون در Response Headers ، یک هدر content-encoding نشان می‌دهد که سرور در حال ارسال این فایل با کدگذاری gzip است.

سربرگ کدگذاری محتوا

فشرده سازی استاتیک

ایده پشت فشرده سازی استاتیک این است که دارایی ها فشرده شده و زودتر ذخیره شوند.

طرفداران

  • تاخیر به دلیل سطوح فشرده سازی بالا دیگر نگران کننده نیست. برای فشرده‌سازی فایل‌ها، نیازی نیست که در لحظه اتفاقی بیفتد، زیرا اکنون می‌توان آنها را مستقیماً واکشی کرد.

منفی

  • دارایی ها باید با هر ساختی فشرده شوند. در صورت استفاده از سطوح فشرده سازی بالا، زمان ساخت می تواند به میزان قابل توجهی افزایش یابد.

فشرده سازی استاتیک با Node/Express و webpack

از آنجایی که فشرده‌سازی استاتیک شامل فشرده‌سازی فایل‌ها از قبل می‌شود، تنظیمات بسته وب را می‌توان برای فشرده‌سازی دارایی‌ها به عنوان بخشی از مرحله ساخت تغییر داد. برای این کار می توان CompressionPlugin استفاده کرد.

با افزودن آن به عنوان devDependency در package.json شروع کنید:

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

مانند هر افزونه webpack دیگری، آن را در فایل تنظیمات webpack.config.js:

const path = require("path");

//...

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

و آن را در آرایه plugins قرار دهید:

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

به طور پیش فرض، افزونه فایل های ساخت را با استفاده از gzip فشرده می کند. به مستندات نگاهی بیندازید تا نحوه افزودن گزینه‌ها برای استفاده از الگوریتم دیگری یا گنجاندن/حذف فایل‌های خاص را بیاموزید.

هنگامی که برنامه بارگیری مجدد و بازسازی می شود، اکنون یک نسخه فشرده از بسته اصلی ایجاد می شود. کنسول Glitch را باز کنید تا به آنچه در داخل دایرکتوری public/ نهایی که توسط سرور Node ارائه می شود نگاهی بیندازید.

  • روی دکمه Tools کلیک کنید.
  • روی دکمه Console کلیک کنید.
  • در کنسول، دستورات زیر را اجرا کنید تا به دایرکتوری public تغییر دهید و همه فایل‌های آن را ببینید:
cd public
ls

فایل های خروجی نهایی در فهرست عمومی

نسخه gzipped بسته، main.bundle.js.gz ، اکنون در اینجا نیز ذخیره می شود. CompressionPlugin همچنین index.html به صورت پیش فرض فشرده می کند.

کار بعدی که باید انجام شود این است که به سرور بگویید هر زمان که نسخه اصلی JS آنها درخواست شد، این فایل های gzip شده را ارسال کند. این را می توان با تعریف یک مسیر جدید در 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 برای یک نقطه پایانی خاص پاسخ دهد استفاده می شود. سپس یک تابع callback برای تعریف نحوه رسیدگی به این درخواست استفاده می شود. مسیر به این صورت عمل می کند:

  • تعیین '*.js' به عنوان اولین آرگومان به این معنی است که برای هر نقطه پایانی که برای واکشی یک فایل JS فعال می شود، کار می کند.
  • در پاسخ به تماس، .gz به URL درخواست وصل شده و سرصفحه پاسخ Content-Encoding روی gzip تنظیم شده است.
  • در نهایت، next() تضمین می کند که دنباله به هر فراخوانی که ممکن است بعدی باشد ادامه می یابد.

پس از بارگیری مجدد برنامه، یک بار دیگر به پنل Network نگاهی بیندازید.

کاهش اندازه بسته نرم افزاری با فشرده سازی استاتیک

درست مثل قبل، کاهش قابل توجهی در اندازه باندل!

نتیجه

این کد لبه فرآیند کوچک سازی و فشرده سازی کد منبع را پوشش می دهد. هر دوی این تکنیک‌ها در حال تبدیل شدن به یک پیش‌فرض در بسیاری از ابزارهای موجود امروزی هستند، بنابراین مهم است که بدانید آیا زنجیره ابزار شما از قبل از آن‌ها پشتیبانی می‌کند یا اینکه باید خودتان شروع به اعمال هر دو فرآیند کنید.