نحوه عملکرد فشردهسازی JavaScript
Terser در چهار مرحله روی درخت نحو انتزاعی کد شما کار میکند. هر مرحله مستقل است، بنابراین میتوانید هر یک را بدون خراب کردن دیگران روشن یا خاموش کنید.
- تجزیه (Parse). Terser JavaScript شما را به یک AST تجزیه میکند. هر خطای نحوی با token و خطی که آن را ایجاد کرده اینجا ظاهر میشود، بنابراین مشکل واقعی را قبل از اجرای هر تبدیلی پیدا میکنید. پارسر هر ساختار استاندارد ECMAScript تا آخرین پیشنهادهای مرحله ۴ را قبول میکند.
- فشردهسازی (Compress). فشردهساز AST را پیمایش میکند و دهها تبدیل حفظکننده معناشناسی اعمال میکند: تا کردن ثابت، حذف شاخه مرده، inline کردن توابع خالص کوتاه، فشردهسازی اعلانهای متوالی
var، و بازنویسی فرمهای معادل جمله (if/elseبه عملگر سهتایی، کوتاهکردن زنجیره مقایسه، فشردهسازی return شرطی). هر تبدیل در اصل قابل برگشت است؛ فشردهساز هرگز رفتار قابل مشاهده را تغییر نمیدهد. - تغییر نام (Mangle). تغییردهنده نام، binding های محلی را به کوتاهترین شناسههای منحصربهفرد (
a،b،c، …) در هر scope تغییر نام میدهد. فقط نامهایی که نمیتوانند از مرز ماژول خارج شوند تغییر نام میگیرند، بنابراین binding های export شده، کلیدهای property و مراجع global دستنخورده باقی میمانند. نتیجه بزرگترین صرفهجویی بایتی هر مرحلهای است. - رندر (Render). Terser AST تبدیلشده را با فضاهای خالی فشردهشده و جداکنندههای جمله کاهشیافته به حداقلی که گرامر قبول میکند به یک رشته JavaScript چاپ میکند. کامنتها حذف میشوند مگر اینکه تغییر حالت حفظ-مجوز بلوکهای
/*! … */را نگه دارد، که اکثر مجوزهای CDN الزام میکنند نگه دارید.
چرا JavaScript را فشرده کنیم
- بارگذاری سریعتر صفحه. اسکریپتهای کوچکتر زودتر تجزیه و اجرا میشوند. در یک اتصال موبایل 4G یک کاهش ۴۰ درصدی بایت، ثانیههای واقعی را از Time to Interactive کم میکند، که معیاری است که PageSpeed Insights گوگل بهشدت اندازه میگیرد.
- کاهش هزینههای egress CDN. CloudFront، Cloudflare و Fastly به ازای هر گیگابایت egress هزینه میگیرند. کاهش ۴۰ درصدی اسکریپت در میلیونها بازدید ماهانه قبل از اینکه هر کار تصویر یا CSS انجام شود به صرفهجوییهای واقعی تبدیل میشود. این حساب حتی پس از gzip و brotli هم درست است — فشردهسازی توکنهایی را حذف میکند که فشردهساز در غیر این صورت باید آنها را کدگذاری میکرد.
- این فشردهساز صفحه شما را کند نمیکند. Terser ~۱ مگابایت غیرفشرده است. اکثر minifier های آنلاین کل کتابخانه را در بارگذاری صفحه ارسال میکنند، که نمره Lighthouse خودشان را خراب میکند و صفحه را قبل از اینکه کاربر چیزی تایپ کند کند میکند. این صفحه Terser را فقط وقتی روی Minify کلیک میکنید یا حالت زنده را روشن میکنید lazy-load میکند — بنابراین رندر اولیه زیر آستانههای Core Web Vitals میماند که خود ابزار وعده میدهد به شما کمک کند به آنها برسید.
- قبولی در Core Web Vitals. Lighthouse و PageSpeed Insights JavaScript بزرگ را بهعنوان یک عامل مستقیم در Total Blocking Time ضعیف علامتگذاری میکنند. فشردهسازی کتابخانههای vendor و bundle های برنامه سریعترین برد در ممیزیهای Lighthouse «کاهش JavaScript استفادهنشده» و «حذف ماژولهای تکراری» است — معمولاً یک کاهش یکبار ارزش ده یا بیست امتیاز.
کاربردهای رایج
فشردهسازی JavaScript در تقریباً هر مرحله از یک پروژه وب مدرن ظاهر میشود.
- هوکهای pre-commit: اسکریپتهای ابزاری فردی را قبل از commit به مخزن فشرده کنید تا artifact commitشده برای پروداکشن آماده باشد و diff تغییرات منطقی را به جای فضای خالی نشان دهد.
- ممیزی widget شخص ثالث: قطعه embed یک vendor را الصاق کنید و بررسی کنید آیا قبلاً فشرده شده یا میتوان آن را قبل از ارائه به میلیونها کاربر بیشتر کوچک کرد.
- پاکسازی اسکریپتهای قدیمی: پلاگینهای jQuery قدیمی و اسکریپتهای دستنوشته که قبل از pipeline ساخت فعلی شما هستند را فشرده کنید، بدون لمس کردن درخت سورس.
یک مثال عملی
یک تابع کوچک در نظر بگیرید: function add(firstNumber, secondNumber) { /* sums two numbers */ return firstNumber + secondNumber; } console.log(add(1, 2)); — حدود ۱۳۰ بایت شامل کامنت. آن را با Mangle و Compress هر دو روشن در بالا الصاق کنید. خروجی به تقریباً function add(n,o){return n+o}console.log(add(1,2)); کوتاه میشود — حدود ۵۵ بایت، کاهش ۵۸ درصدی. نام تابع add باقی میماند زیرا در فراخوانی console.log مرجع است؛ نام پارامترهای firstNumber و secondNumber به حروف واحد کوتاه میشوند زیرا برای بدنه تابع محلی هستند. Mangle را خاموش کنید تا نامهای پارامتر خوانا بمانند در حالی که هنوز فضاهای خالی فشرده شده و کامنت حذف میشود.
FAQ
آیا این در مرورگر من اجرا میشود؟
بله. Terser دفعه اول که روی Minify کلیک میکنید یا حالت زنده را فعال میکنید lazy-load میشود — حدود ۲۰۰ کیلوبایت فشرده در cache مرورگر شما قرار میگیرد، سپس چیزی بیشتر دانلود نمیشود. کد شما هرگز صفحه را ترک نمیکند.
تغییر نام (mangle) چیست و آیا ایمن است؟
mangle متغیرهای محلی را به حروف واحد تغییر نام میدهد تا بایت صرفهجویی کند. برای اسکریپتهای خودکفا و bundle های IIFE ایمن است زیرا تغییر نامها هرگز از scope خارج نمیشوند. برای اسکریپتهایی که global ها را با نام افشا میکنند (مثلاً window.myLib = …) بدون یک wrapper ایمن نیست. وقتی مطمئن نیستید Mangle را خاموش کنید.
چرا کدم پس از فشردهسازی خراب شد؟
سه مظنون رایج: eval یا with که متغیرها را با رشته مرجع میدهد؛ خواندنهای Function.name یا arguments.callee که به شناسه اصلی وابسته هستند؛ یا global هایی که با نامی که تغییر پیدا کرده افشا شدهاند. ابتدا Mangle را خاموش کنید تا ببینید آیا تغییر نام یا یک تبدیل Compress علت است.
آیا از نحو مدرن (ES2020+) پشتیبانی میکند؟
بله. هدف ECMAScript را به ES2020 یا Next تنظیم کنید و Terser optional chaining، nullish coalescing، top-level await و عملگرهای logical-assignment را حفظ میکند. به ES5 تنظیم کنید و Terser جایی که میتواند down-compile میکند، اما transpiler کامل نیست — از Babel برای نحوی که ES5 اصلاً نمیتواند نشان دهد استفاده کنید.
فشردهسازی JavaScript سمت مرورگر با Terser خروجی با کیفیت پروداکشن بدون اضافه کردن یک build tool میدهد. یک اسکریپت الصاق کنید، هدف ECMAScript را انتخاب کنید، نتیجه را کپی یا دانلود کنید. هیچ آپلود، بدون حساب کاربری، بدون build pipeline. خود فشردهساز فقط وقتی از آن بخواهید بارگذاری میشود — بنابراین باز کردن این صفحه چند کیلوبایت هزینه دارد، نه یک مگابایت.