6 دقیقه مطالعه
مشکل Memory Leak هنگام استفاده از hash در Webpack
در این مقاله یک مشکل رایج Memory Leak در Webpack هنگام استفاده از contenthash در محیط توسعه بررسی میشود و راهحل عملی ارائه میدهیم.
توی این پست سعی میکنیم یک مشکل رایج توی محیط توسعه با webpack رو بررسی کنیم که وقتی از ویژگی cacheشکنش استفاده میکنم و از [hash] های توی اسم فایل ها استفاده میکنیم منجر به نشت حافظه (Memory Leak) میشه.
مشکل چیه؟ کشینگ چه مشکلی ایجاد میکنه؟
مرورگرها فایل های استاتیک مثل JavaScript، CSS، fonts و تصاویر رو کش میکنن تا در دفعات بعدی یا بازدید های بعدی کاربر، نیازی به دانلود مجدد اینها نباشه و سرعت لود سایت بره بالا. اما اگر تغییری توی کد های یک فایل انحام بدیم و اسم فایل تغییر نکنه، مرورگر نسخه کششده رو نمایش میده و تغییرات جدید دیده نمیشن.
برای حل این مشکل، Webpack قابلیت اضافه کردن hash به اسم فایلها رو ارائه کرد که انواع مختلفی هم داره:
- [hash]
- [chunkhash]
- [contenthash]
با استفاده از این ویژگیها، اسم فایل با هر تغییر در محتوا عوض میشه و مرورگر فایل جدید را دانلود میکند.
مشکل: Memory Leak در محیط توسعه (Development)
در ابتدا Webpack از [hash] استفاده میکرد. اما در محیط توسعه باعث ایجاد مشکل شد:
وبپک برای هر بیلد که میتونه شامل کلی chunk یا فایل باشه یک هش در نظر میگیره و به اسم تمامی فایل ها اضافه میکنه و باعث میشه همه فایل ها اسمشون تغییر کنه و فایلهای جدیدی تولید بشه و فایلهای قبلی پاک نشه و باعث میشه نشت حافظه اتفاق بیوفته
به صورت زیر:
- Webpack یک بیلد جدید تولید میکنه.
- اسم فایلها بهدلیل تغییر
hashعوض میشه. - فایلهای جدید در حافظه (Memory) ذخیره میشن.
- فایلهای قدیمی حذف نمیشن.
مثال:
Build 1 → main.a1b2c3.js
Build 2 → main.d4e5f6.js
main.a1b2c3.js هنوز در حافظه باقی مونده و main.d4e5f6.js هم بهش اضافه میشه.
✅ آیا در Production هم این مشکل وجود دارد؟
خیر. در حالت Production:
- Webpack فقط یک بار اجرا میشود.
- هیچ Watch یا DevServer فعالی وجود ندارد.
- فایلها روی دیسک ذخیره میشوند.
- بعد از پایان build، process خاتمه مییابد و حافظه آزاد میشود.
پس در حالت عادی، [hash] در production مشکلی ایجاد نمیکند.
🛠️ نکاتی برای توسعهدهندگان
- اگر در محیط توسعه هستید، از
[hash]استفاده نکنید. - در عوض، از
[contenthash]یا[chunkhash]فقط در production استفاده کنید. - میتوانید
devtoolرا رویevalیاcheap-module-source-mapتنظیم کنید تا performance بهتری داشته باشید. - از قابلیت persistent caching در Webpack 5 فقط با مدیریت خوب فایلهای کش استفاده کنید. در غیر این صورت، باعث پر شدن دیسک میشود.
🔚 نتیجهگیری
استفاده از [hash] در Webpack مزایای مهمی برای کش دارد، اما در محیط توسعه ممکن است باعث نشت حافظه شود، چون فایلهای بیاستفاده در حافظه باقی میمانند و آزاد نمیشوند. راهحل ساده است: در dev از hash استفاده نکنید و فقط در production آن را فعال کنید.
📖 اگر با مفاهیم نسخهبندی معنایی (SemVer) در npm آشنا نیستید، پست SemVer توی npm به زبان ساده رو بخونید.
نگاهی عمیقتر: چرا GC نمیتواند فایلهای قدیمی را پاک کند؟
مرورگر فایلهای استاتیک (JS, CSS, Images, ...) رو از سرور دریافت میکنه و اونها رو cache میکنه تا در درخواستهای بعدی نیازی به دانلود مجدد نباشه.
پلاگینهایی مثل HtmlWebpackPlugin و MiniCssExtractPlugin، هنگام build، خروجیها رو در حافظه نگه میدارن برای پردازش HTML، CSS و... اگر در هر build خروجی جدیدی با hash متفاوت بیاد، اون پلاگینها فایل جدید رو اضافه میکنن ولی قبلی رو آزاد نمیکنن.
هر object جاوااسکریپت (مثل buffer فایل) تا زمانی که Reference بهش وجود داره، توسط Garbage Collector پاک نمیشه. پلاگینها و cache loaderها درون خودشون یک reference به فایل قبلی نگه میدارن (برای diff یا comparison یا track changes). چون [hash] باعث میشه فایلها هر بار جدید باشن، chain این referenceها بزرگ و بزرگتر میشه.
این پلاگین صرفاً وظیفه دارد فایل را کپی کند و با هر بار بیلد فایل را تغییر نمیدهد. این کار باعث نمیشود نشت حافظه اتفاق بیفتد.
❓ آیا استفاده از [hash] در محیط Production باعث Memory Leak میشود؟
✅ پاسخ کوتاه:
خیر، در حالت عادی در محیط Production استفاده از [hash] باعث memory leak نمیشود، چون:
- در production، Webpack فقط یک بار اجرا میشود (نه بهصورت دائمی مثل dev-server).
- هیچ Watch یا حافظهای برای نگهداشتن فایلها وجود ندارد.
- فایلها بهصورت فیزیکی روی دیسک نوشته میشوند.
- بعد از پایان build، process از بین میرود و حافظه پاک میشود.
در حالت dev-server:
وقتی پروژه بالا میاد با تغییر در حتی یک فایل، پس از ریبیلد شدن فقط همان فایل بررسی و بیلد میشود و دوباره ایجاد میشود نه همه فایلها. ولی چون اسم فایل hash داره و تغییر کرده، فایل قبلی توی حافظه باقی میمونه.
📦 حالا کی باید از [hash] استفاده کنیم؟
معمولاً موقعی که داری فایلهایی مثل JavaScript و CSS رو برای production build میکنی، از [hash] یا [contenthash] استفاده کن.
output: {
filename: '[name].[contenthash].js',
clean: true
}
اینطوری:
- اگه فایل تغییر کنه → hash تغییر میکنه → اسم فایل تغییر میکنه → مرورگر میفهمه نسخهی جدید اومده
- اگه فایل تغییر نکنه → hash ثابت میمونه → فایل از cache استفاده میشه
🔁 مرورگر چطوری cache میکنه؟
مرورگر از HTTP Headers استفاده میکنه که از طرف سرور فرستاده میشن:
🕐 Cache-Control: max-age
Cache-Control: public, max-age=31536000
یعنی این فایل تا یک سال cache بشه. اگه فایل hash داشته باشه، مشکلی پیش نمیآید چون فایل جدید hash جدید داره و مرورگر نسخه قبلی رو پاک میکنه.
🏷️ ETag و Last-Modified
- ETag: یه hash از محتوا رو برمیگردونه. مرورگر دفعه بعد میپرسه "آیا هنوز همون فایله؟" اگه بله → سرور 304 میده.
- Last-Modified: تاریخ آخرین تغییر رو چک میکنه.
ولی وقتی hash تو اسم فایل هست، دیگه نیازی به اینها نیست چون خود اسم فایل داره به مرورگر میگه که این یه نسخهی جدیده.
🧠 مثال کامل Webpack و Express برای کش درست:
// webpack.prod.js
module.exports = {
mode: "production",
output: {
filename: "[name].[contenthash].js",
clean: true,
},
};
// Express
app.use(
express.static("dist", {
maxAge: "1y",
etag: false,
})
);
وقتی داری توسعه میدی، از hash استفاده نکن. فقط یه اسم ساده بذار مثل [name].js. ولی توی production، hash خیلی مفیده چون دقیقاً به مرورگر میگه که آیا این فایل تغییر کرده یا نه. اگر dev-server داری و میبینی حافظه کم میاد یا process سنگین شده، بدون شک ممکنه memory leak از همین hash باشه.
نوشتههای مرتبط
شاید این نوشتهها هم برات مفید باشن
Semantic Versioning
مقدمهای سریع بر نسخهبندی معنایی چیه و چرا ازش استفاده میکنیم.
خواندن نوشته ←SemVer توی npm به زبان ساده
توی این پست یاد میگیریم علامتهای ^ و ~ توی نسخهگذاری npm دقیقاً چه کاری انجام میدن و npm install چطور باهاشون رفتار میکنه.
خواندن نوشته ←