چگونه یک پکیج جاوا اسکریپت را هم برای node.js و هم برای browser بنویسیم؟

خیلی وقت ها پیش میاد که میخواییم یه package جی‌اس بنویسم که هم داخل مرورگر کار کنه و هم داخل node.js. برای دولوپر های فصلی (کسایی که به دلیلی مجور شدن به صورت موقت JS کد بزنن) شاید کمی گیج کننده باشه.

پکیج ایزومورفیک
پکیج ایزومورفیک

یه مشکلی وجود داره! پیاده سازی اون ماژولی که می‌خوایید داخل npm منتشر کنید در دو محیط node.js و browser یکمی متفاوته. این وضعیت مکرر پیش میاد و پیاده سازی درست اون برای هر دو محیط یکمی مشکل سازه. بخصوص زمانی که بخوایید حجم bundle تون در مرورگر بهینه باشه و کد های اضافی node.js را نداشته باشه.

یه پکیج ساده جاوا اسکریپت بسازیم

یه پکیج خیلی کوچیک قراره بسازیم که یه string به عنوان ورودی بگیره و در خروجی کد شده‌ی اونو به صورت کد شده از نوع base64 برگردونه.

در مرورگر یه تابع داخلی داریم به اسم btoa که دقیقا همین کار را انجام میده:

در node.js ما تابعی با این نام نداریم و باید با استفاده از Buffer بصورت زیر این کار را انجام بدیم:

اگه ما اسم پکیج را بزاریم base64-encode-string و در کدمون ازش استفاده کنیم خروجی اینجوریه:

حالا ما باید تشخیص بدیم که آیا کدمون در محیط node.js داره اجرا میشه یا browser. هم webpack و هم Browserify یه متغییر گلوبال به نام process.browser تعریف می‌کنن که اگه کد داخل مرورگر اجرا بشه مقدارش true و اگه داخل node.js اجرا بشه مقدارش false هست. بنابراین اینجوری باید پیاده سازی کنیم:

حالا ما یه فایل داریم به نام index.js و زمانی که npm push را بزنیم پکیج میره روی npm ولی با این که کد ما به درستی کار می‌کنه یه مشکل پرفورمنسی بزرگ داره. مشکل اینه که این پکیج ما برای مرورگر خیلی سنگینه چون ما از عناصر داخلی node.js مثل process , Buffer استفاده کردیم، هم webpack و هم Browserify آن عناصر را به باندل اضافه میکنن. یعنی باندل شده‌ی این 9 خط کد 24.7 کیلوبایت (درحالت minify شده) حجم دارد.

فیلد browser چگونه به ما کمک می‌کند

من یکمی داخل دایکیومنت های Browserify و Webpack گشتم تا ببینم نکته‌ای ترفندی چیزی هست که بشه این مشکل را حل کرد تا این ریپو را کشف کردم. یه فیلد داخل package.json به نام browser اضافه می‌کنیم که آدرس اون ماژولی که قراره در مرورگر اجرا بشه را بهش می‌دیم و باندلر از این طریق زمانی که داره بیلد می‌کنه میفهمه که برای مرورگر باید یه باندل کم حجم درست کنه:

بنابراین باید فانکشن‌ها را هم از هم جدا کرد:

حالا زمانی که بیلد می‌کنیم حجم کل باندل حدود 511 بایت می‌شود! زمانی که این پکیج یعنی base64-encode-string را به پروژه اضافه می‌کنیم اگه در محیط node.js باشیم نسخه مخصوص آن و اگه داخل مرورگر باشیم نسخه مخصوص مرورگر به درستی اجرا می‌شود.