Bundle Size واقعاً چقدر مهم است؟ وقتی چند KB میتواند روی Performance اثر بگذارد
وقتی صحبت از Performance یک وبسایت میشود، معمولاً اولین چیزی که به ذهن میرسد سرعت اینترنت، سرور یا حجم تصاویر است. اما یکی از مهمترین عواملی که گاهی نادیده گرفته میشود، حجم JavaScript و Bundle نهایی سایت است.
ممکن است اضافه شدن چند KB به Bundle در نگاه اول بیاهمیت به نظر برسد. اما وقتی همین چند KB باید توسط هزاران یا میلیونها کاربر دانلود، Parse، Compile و Execute شود، تأثیر آن میتواند بسیار بیشتر از چیزی باشد که تصور میکنیم.
به همین دلیل، در پروژههای مدرن React، Next.js و سایر فریمورکهای JavaScript، مدیریت Bundle Size فقط یک موضوع فنی نیست؛ بلکه بخشی از استراتژی Performance محصول محسوب میشود.
در این مقاله بررسی میکنیم که Bundle Size دقیقاً چیست، چرا اهمیت دارد، چند KB اضافه چه تأثیری روی Performance میگذارد و چگونه میتوان حجم JavaScript را کاهش داد.
Bundle Size چیست؟
قبل از بررسی تأثیر Bundle Size، باید بدانیم دقیقاً درباره چه چیزی صحبت میکنیم.
در یک پروژه مدرن JavaScript معمولاً تعداد زیادی فایل و Dependency داریم:
src/
├── components/
├── pages/
├── hooks/
├── utils/
└── services/
همچنین ممکن است پروژه از کتابخانههایی مانند:
React
React Router
UI Library
Date Library
Chart Library
Animation Library
استفاده کند.
Bundler این فایلها و وابستگیها را تحلیل کرده و در نهایت آنها را برای استفاده در مرورگر به فایلها یا Chunkهای قابل دریافت تبدیل میکند.
برای مثال:
Application
↓
Dependencies
↓
Bundler
↓
JavaScript Chunks
↓
Browser
حجم این فایلهای خروجی بخشی از چیزی است که به عنوان Bundle Size درباره آن صحبت میکنیم.
چرا چند KB مهم است؟
ممکن است بگوییم:
«۵۰KB که چیزی نیست!»
اما مسئله فقط عدد روی فایل نیست.
مرورگر برای استفاده از JavaScript باید چند مرحله را طی کند:
Download
↓
Parse
↓
Compile
↓
Execute
بنابراین یک فایل JavaScript بزرگتر فقط به معنی دانلود اطلاعات بیشتر نیست.
کد بیشتر میتواند به معنی:
زمان دانلود بیشتر
Parse بیشتر
Compile بیشتر
اجرای JavaScript بیشتر
مصرف CPU بیشتر
مصرف باتری بیشتر
باشد.
به همین دلیل Performance را نباید فقط بر اساس حجم فایل اندازهگیری کرد.
Bundle Size با Transfer Size یکی نیست
یکی از اشتباهات رایج این است که حجم فایل روی دیسک را با حجم داده منتقلشده یکی بدانیم.
برای مثال ممکن است یک فایل JavaScript حجم زیر را داشته باشد:
500 KB
اما با Brotli یا Gzip حجم انتقالیافته بسیار کمتر شود:
150 KB
بنابراین دو مفهوم مهم داریم:
Uncompressed Size
حجم واقعی فایل پس از Build.
Transfer Size
حجمی که واقعاً از طریق شبکه به مرورگر منتقل میشود.
اما این موضوع به این معنی نیست که حجم اولیه مهم نیست.
مرورگر پس از دریافت فایل فشرده باید آن را Decode کرده و سپس JavaScript را Parse و Execute کند.
بنابراین کاهش حجم واقعی Bundle همچنان ارزشمند است.
چند KB اضافه دقیقاً چه مشکلی ایجاد میکند؟
پاسخ به این سؤال به شرایط کاربر بستگی دارد.
برای یک کاربر با:
اینترنت سریع
CPU قدرتمند
موبایل جدید
ممکن است افزایش ۳۰ یا ۵۰KB تقریباً محسوس نباشد.
اما برای کاربری با:
اینترنت ضعیفتر
دستگاه اقتصادی
CPU ضعیف
حافظه محدود
همین افزایش میتواند هزینه بیشتری داشته باشد.
این موضوع اهمیت یک اصل مهم در Web Performance را نشان میدهد:
Performance فقط درباره سرعت سرور نیست؛ درباره توانایی دستگاه کاربر برای دریافت و پردازش صفحه نیز هست.
JavaScript فقط دانلود نمیشود؛ اجرا هم میشود
فرض کنید دو سایت داریم:
Site A
JavaScript = 100 KB
Site B
JavaScript = 500 KB
ممکن است هر دو سایت روی یک سرور سریع قرار داشته باشند.
اما سایت دوم JavaScript بسیار بیشتری برای پردازش در اختیار مرورگر قرار میدهد.
در نتیجه ممکن است زمان بیشتری برای:
Parse
Compile
Execute
صرف شود.
این موضوع روی تعاملپذیری صفحه تأثیر میگذارد.
TBT و INP چه ارتباطی با JavaScript دارند؟
برای بررسی Performance، فقط سرعت نمایش صفحه مهم نیست.
معیارهایی مانند INP (Interaction to Next Paint) نیز اهمیت دارند.
INP بررسی میکند که صفحه پس از تعامل کاربر چقدر سریع به آن واکنش نشان میدهد.
اگر JavaScript زیادی در Main Thread اجرا شود، ممکن است تعامل کاربر با تأخیر مواجه شود.
مثلاً کاربر روی یک Button کلیک میکند:
User Click
↓
JavaScript Busy
↓
Task اجرا میشود
↓
Browser دوباره آزاد میشود
↓
UI Update
اگر Main Thread برای مدت زیادی مشغول باشد، کاربر احساس میکند سایت کند است.
بنابراین حتی اگر صفحه سریع نمایش داده شود، JavaScript سنگین میتواند تجربه تعامل را خراب کند.
Main Thread چرا مهم است؟
بخش مهمی از پردازش JavaScript در Main Thread مرورگر انجام میشود.
اگر JavaScript زیادی برای اجرا وجود داشته باشد، Main Thread ممکن است مشغول بماند.
برای مثال:
Main Thread
────────────────────────────
JavaScript Task
████████████████████
User Interaction
↑
منتظر میماند
هرچه Taskهای JavaScript سنگینتر باشند، احتمال ایجاد تأخیر در تعامل بیشتر میشود.
به همین دلیل کاهش Bundle Size میتواند یکی از ابزارهای کاهش هزینه پردازش JavaScript باشد.
Bundle بزرگ همیشه بد است؟
نه.
این نکته بسیار مهم است.
هدف Performance این نیست که:
«هر طور شده Bundle را به کمترین حجم ممکن برسانیم.»
هدف این است که:
«فقط JavaScript مورد نیاز را، در زمان مناسب، برای کاربر ارسال و اجرا کنیم.»
ممکن است یک Application پیچیده به JavaScript بیشتری نیاز داشته باشد.
مشکل زمانی ایجاد میشود که کاربر مجبور باشد مقدار زیادی JavaScript را دانلود و اجرا کند، در حالی که در آن لحظه به آن نیاز ندارد.
Code Splitting؛ راهحل Bundleهای بزرگ
یکی از مهمترین روشها برای کنترل Bundle Size، Code Splitting است.
به جای اینکه تمام کد را در یک Bundle بزرگ قرار دهیم:
app.js
──────────────
500 KB
میتوان آن را به چند بخش تقسیم کرد:
main.js 120 KB
dashboard.js 90 KB
editor.js 150 KB
admin.js 140 KB
در این حالت کاربر میتواند فقط بخش مورد نیاز را دریافت کند.
مثلاً کاربری که وارد Dashboard نشده، لزوماً نباید کد مربوط به Editor یا Admin را دانلود کند.
Lazy Loading چگونه کمک میکند؟
Lazy Loading یعنی بخشی از کد را فقط زمانی بارگذاری کنیم که واقعاً لازم است.
مثلاً:
const Editor = lazy(() => import('./Editor'));
در این حالت کد Editor میتواند در یک Chunk جدا قرار بگیرد.
وقتی کاربر واقعاً به Editor نیاز پیدا کند، مرورگر آن Chunk را دریافت میکند.
این رویکرد مخصوصاً برای بخشهایی مانند:
ویرایشگرها
نمودارها
پنل مدیریت
Modalهای پیچیده
ابزارهای تخصصی
صفحات کمتر استفادهشده
مناسب است.
Tree Shaking و Bundle Size
یکی دیگر از تکنیکهای مهم، Tree Shaking است.
فرض کنید یک کتابخانه چندین قابلیت دارد:
export function chart() {}
export function table() {}
export function editor() {}
export function calendar() {}
اما پروژه شما فقط از:
import { chart } from 'library';
استفاده میکند.
اگر کتابخانه و ابزار Build بهدرستی از Tree Shaking پشتیبانی کنند، بخشهای استفادهنشده میتوانند از Bundle حذف شوند.
در نتیجه:
Library
│
├── chart ← Used
├── table ← Removed
├── editor ← Removed
└── calendar ← Removed
این موضوع میتواند حجم JavaScript نهایی را به شکل قابل توجهی کاهش دهد.
Dependencyهای کوچکتر همیشه بهترند؟
لزومی ندارد.
گاهی یک کتابخانه بزرگ، قابلیت Tree Shaking بسیار خوبی دارد و فقط بخش کوچکی از آن وارد Bundle میشود.
در مقابل، ممکن است یک کتابخانه ظاهراً کوچک، به شکلی طراحی شده باشد که بخش زیادی از آن همیشه وارد Bundle شود.
بنابراین هنگام انتخاب Dependency بهتر است فقط به حجم Package نگاه نکنیم.
مواردی مانند:
ESM Support
Tree Shaking
Dependencyهای داخلی
کیفیت Bundle
اندازه واقعی خروجی
میزان استفاده در پروژه
اهمیت دارند.
یک مثال واقعی
فرض کنید برای یک قابلیت ساده، کتابخانهای با حجم قابل توجه اضافه کردهایم:
import dateLibrary from 'large-date-library';
در حالی که تنها کاری که انجام میدهیم:
formatDate(date);
است.
ممکن است یک راهحل سبکتر یا یک API قابل Tree Shake شدن وجود داشته باشد.
در چنین شرایطی، حذف یا جایگزینی Dependency میتواند بسیار مؤثرتر از بهینهسازیهای جزئی باشد.
Bundle Analyzer چیست؟
اگر بخواهیم Bundle را بهینه کنیم، ابتدا باید بفهمیم چه چیزی آن را بزرگ کرده است.
اینجاست که ابزارهای Bundle Analyzer وارد میشوند.
این ابزارها معمولاً ساختار Bundle را به شکل بصری نمایش میدهند:
Bundle
│
├── React 80 KB
├── UI Library 120 KB
├── Chart Library 250 KB
├── Date Library 90 KB
└── Application 100 KB
با چنین گزارشی میتوان فهمید بزرگترین مصرفکننده Bundle کدام Dependency است.
به جای حدس زدن، ابتدا اندازهگیری میکنیم و سپس تصمیم میگیریم.
چگونه Bundle Size را کاهش دهیم؟
برای کاهش حجم JavaScript میتوان چند رویکرد را همزمان استفاده کرد.
۱. Dependencyهای غیرضروری را حذف کنید
اگر کتابخانهای فقط برای یک قابلیت بسیار ساده استفاده میشود، بررسی کنید آیا واقعاً به آن نیاز دارید یا خیر.
۲. از Tree Shaking استفاده کنید
کتابخانههای مدرن و ESM-friendly انتخاب کنید.
۳. Code Splitting انجام دهید
کدهای مربوط به بخشهای مختلف Application را به Chunkهای مناسب تقسیم کنید.
۴. Lazy Loading را جدی بگیرید
هر چیزی لازم نیست از همان لحظه اول دانلود شود.
۵. Importهای خود را بررسی کنید
گاهی نحوه Import کردن یک کتابخانه روی حجم Bundle تأثیر دارد.
۶. Third-party Scriptها را کنترل کنید
Analytics، Chat Widget، تبلیغات، Tracking و ابزارهای دیگر میتوانند JavaScript قابل توجهی به صفحه اضافه کنند.
۷. Bundle را اندازهگیری کنید
بدون اندازهگیری، بهینهسازی بیشتر شبیه حدس زدن است.
در Next.js چه اهمیتی دارد؟
در پروژههای Next.js، مدیریت JavaScript اهمیت ویژهای دارد؛ زیرا معماری مدرن آن امکان کنترل بهتر روی نحوه ارسال کد به Client را فراهم میکند.
استفاده صحیح از:
Server Components
Dynamic Imports
Code Splitting
Lazy Loading
Client Components فقط در مواقع ضروری
میتواند مقدار JavaScript ارسالشده به مرورگر را کاهش دهد.
یک اصل مهم در معماری Next.js این است:
هر چیزی که لازم نیست روی Client اجرا شود، نباید بدون دلیل به Client منتقل شود.
این موضوع میتواند تفاوت قابل توجهی در Performance ایجاد کند.
Bundle Size را با چه عددی بسنجیم؟
یک عدد جادویی برای تمام پروژهها وجود ندارد.
اینکه:
100 KB
خوب است یا بد، به نوع Application، کاربران، دستگاهها و شرایط شبکه بستگی دارد.
به جای تمرکز روی یک عدد ثابت، بهتر است روند تغییرات را بررسی کنیم.
مثلاً:
Version 1 → 180 KB
Version 2 → 240 KB
Version 3 → 410 KB
اگر بدون اضافه شدن قابلیت مهم، Bundle از 180KB به 410KB رسیده باشد، باید علت آن بررسی شود.
Budget برای Bundle تعریف کنید
یکی از روشهای حرفهای برای جلوگیری از رشد کنترلنشده Bundle، تعریف Performance Budget است.
برای مثال تیم میتواند تصمیم بگیرد:
Initial JavaScript
≤ 200 KB
Critical CSS
≤ 50 KB
Main Page Assets
≤ مشخصشده توسط تیم
این محدودیتها باعث میشوند افزایش حجم Bundle در فرآیند توسعه سریعتر دیده شود.
به جای اینکه بعد از چند ماه متوجه شویم Application سنگین شده است، از ابتدا رشد آن را کنترل میکنیم.
مهمترین اشتباه در بهینهسازی Bundle
بزرگترین اشتباه این است که بدون اندازهگیری شروع به حذف یا تغییر کد کنیم.
مثلاً:
«این کتابخانه احتمالاً سنگین است، حذفش کنیم.»
این روش همیشه درست نیست.
روش بهتر:
Measure
↓
Identify
↓
Analyze
↓
Optimize
↓
Measure Again
ابتدا باید بدانیم مشکل کجاست.
آیا کاهش Bundle همیشه باعث افزایش محسوس سرعت میشود؟
نه لزوماً.
اگر Bundle را از:
101 KB
به:
96 KB
کاهش دهیم، ممکن است در بسیاری از شرایط تفاوت محسوسی ایجاد نشود.
اما اگر:
800 KB
JavaScript غیرضروری را به:
300 KB
برسانیم، احتمال تأثیرگذاری بسیار بیشتر است.
پس باید به مقدار، نوع کد، دستگاه کاربر و زمان اجرای آن توجه کنیم.
چند KB در مقیاس بزرگ چه معنایی دارد؟
فرض کنید یک سایت روزانه تعداد بسیار زیادی بازدید دارد.
اگر در هر Page Load فقط:
50 KB
JavaScript غیرضروری ارسال شود، این مقدار در مقیاس بزرگ به حجم قابل توجهی از داده تبدیل میشود.
اما موضوع فقط هزینه انتقال داده نیست.
این JavaScript باید توسط دستگاه کاربران نیز پردازش شود.
بنابراین یک بهینهسازی کوچک در سطح هر درخواست میتواند در مقیاس بزرگ ارزش قابل توجهی داشته باشد.
جمعبندی
Bundle Size یکی از فاکتورهای مهم Performance در وب مدرن است، اما نباید آن را صرفاً به یک عدد تبدیل کنیم.
یک Bundle بزرگ میتواند باعث افزایش:
زمان انتقال
Parse
Compile
Execute
مصرف CPU
و تأخیر در تعامل
شود.
با این حال، هدف اصلی این نیست که Bundle را صرفاً کوچک کنیم؛ هدف این است که JavaScript غیرضروری را ارسال و اجرا نکنیم.
برای رسیدن به این هدف میتوان از مجموعهای از تکنیکها استفاده کرد:
Tree Shaking
+
Code Splitting
+
Lazy Loading
+
Dependency Optimization
+
Server Components
+
Bundle Analysis
در نهایت یک اصل ساده وجود دارد:
هر KB مهم نیست؛ اما هر KB غیرضروری میتواند هزینهای داشته باشد.
وقتی همین چند KB در میلیونها Page Load تکرار میشود و علاوه بر دانلود باید توسط CPU دستگاه کاربر نیز پردازش شود، بهینهسازی Bundle دیگر یک جزئیات کوچک نیست؛ بلکه بخشی از معماری یک محصول سریع و حرفهای است.
