مایکروسافت چطور clarity.js رو ۴.۵ درصد کوچیکتر کرد
خلاصهٔ کاملتر
clarity.js اسکریپتیه که روی سایتهایی که از Microsoft Clarity استفاده میکنن اجرا میشه و رفتار کاربرها رو ثبت میکنه. چون روی میلیاردها صفحه لود میشه، هر بایتش مهمه. تیم Clarity تو این مقاله تعریف میکنه که کار رو به یه حلقهٔ خودکار سپردن: ایجنت کدنویسی pi با ابزار autoresearch یه تغییر میداد، اسکریپت رو بیلد میکرد، حجم gzip رو میسنجید، تستها رو اجرا میکرد و بعد تغییر رو نگه میداشت یا برمیگردوند. بعد از بیش از ۵۰ آزمایش، اسکریپت ۱۲۵۷ بایت gzip (یعنی ۴.۵ درصد) سبکتر شد.
نکتهٔ جالب اینه که gzip الگوهای تکراری رو با یه ارجاع کوتاه جایگزین میکنه. پس کد تکراری تقریباً مجانیه، ولی هر اسم تابع یا الگوی جدید هزینه داره. تیم چهار بلوک ششخطی یکسان رو تو یه تابع کمکی coordinates() ریخت؛ حجم خام ۲۶۴ بایت کم شد ولی حجم gzip ۲۳ بایت بیشتر شد. حتی ||= که سینتکس کوتاهتریه بدتر بود، چون الگوی قدیمی x = x || defaultValue اونقدر تو کد تکرار شده بود که gzip ازش ارزونتر نگهداری میکرد.
بزرگترین برد (۴۴۴ بایت، یعنی ۳۵ درصد کل صرفهجویی) از حذف import namespace بود. وقتی import * as مینویسین و خود اون namespace رو مثل یه مقدار جابهجا میکنین (مثلاً تو یه آرایه میذارین)، Rollup مجبوره یه آبجکت wrapper برای همهٔ exportهای ماژول بسازه. تیم این الگو رو با importهای اسمدار عوض کرد:
import { start as diagStart, stop as diagStop } from "@src/diagnostic";
const modules = [{ start: diagStart, stop: diagStop }, …];فقط فایل data/index.ts با ۱۳ import namespace تو یه جدول dispatch، ۳۲۴ بایت از این صرفهجویی رو داشت. ولی به گفتهٔ تیم، اگه فقط به propertyها دسترسی دارین (مثل module.start()) مشکلی نیست، چون Rollup خودش فراخوانیها رو inline میکنه.
بقیهٔ صرفهجوییها اینا بودن: تنظیمات terser شامل passes: 3 و hoist_vars: true و freeze: false (۳۰۱ بایت)؛ تبدیل template literalها به جمع ساده با +، چون TypeScript برای هدف ES5 اونا رو به زنجیرهٔ concat تبدیل میکرد (۱۱۰ بایت)؛ جایگزینی یه بلوک ۲۷ خطی با Object.assign (۱۰۹ بایت)؛ و حذف کامل تابعهای اضافه (۸۴ بایت). مثلاً یه تابع ۱۲ خطی خوندن stream با یه خط عوض شد:
return new Uint8Array(await new Response(stream).arrayBuffer());بزرگترین برد ممکن ولی منتشر نشد. بالا بردن target تایپاسکریپت از ES5 به ES2017 و ES2020، polyfillهای __awaiter و __generator رو حذف میکرد و ۲۳۰۸ بایت صرفهجویی داشت، تقریباً دو برابر همهٔ بقیه. اما Zone.js (کتابخونهای که Angular باهاش Promiseها رو رهگیری میکنه) انتظار خروجی ES5 داره و با async/await بومی، change detection انگولار بیصدا خراب میشه. هر چرخهٔ بیلد و اندازهگیری حدود ۳۵ ثانیه طول میکشه، یعنی ایجنت میتونه ۱۰۰ ایده رو زیر یه ساعت تست کنه. توصیهٔ تیم اینه که همیشه حجم gzip رو بسنجین نه حجم خام، و این آزمایشهای ریز رو خودکار کنین.
نکات کلیدی:
- clarity.js بعد از بیش از ۵۰ آزمایش خودکار ۱۲۵۷ بایت gzip (۴.۵ درصد) کوچیکتر شد.
- استخراج تابع کمکی coordinates() حجم خام رو ۲۶۴ بایت کم کرد ولی حجم gzip رو ۲۳ بایت بیشتر کرد.
- حذف import * as در جاهایی که namespace مثل مقدار استفاده میشد ۴۴۴ بایت صرفهجویی داشت.
- تنظیمات terser شامل passes: 3 و hoist_vars: true و freeze: false حدود ۳۰۱ بایت کم کرد.
- ارتقای target به ES2017 تا ۲۳۰۸ بایت صرفهجویی داشت ولی بهخاطر ناسازگاری با Zone.js در Angular منتشر نشد.




