اوه CommonJS! چرا با من MESM میکنی؟! دلایل کنار گذاشتن CommonJS

یک روز وصله معمولی بود. من وابستگیهای npm خود را بدون تغییر کد وصله و ارتقاء دادم و ناگهان، برخی از تستهای واحد من ناموفق بود.
Wtf!
آزمایشات من شکست خورد زیرا Jest encountered an unexpected token
; آنها شکست خوردند زیرا Jest نمی تواند بسته های فقط ESM را خارج از جعبه کنترل کند. در واقع جست در CommonJS نوشته شده است.
اما به چه معنا است؟ برای انجام این کار، باید بدانیم که چرا CommonJS و ESM وجود دارند.
چرا ما به سیستم های ماژول نیاز داریم؟
در روزهای اولیه توسعه وب، جاوا اسکریپت عمدتاً برای دستکاری Document Object Model (DOM) با کتابخانه هایی مانند jQuery استفاده می شد. با این حال، معرفی Node.js همچنین منجر به استفاده از جاوا اسکریپت برای برنامه نویسی سمت سرور شد. این تغییر پیچیدگی و اندازه پایگاه های کد جاوا اسکریپت را افزایش داد. در نتیجه، نیاز به یک روش ساختاریافته برای سازماندهی و مدیریت کد جاوا اسکریپت وجود داشت. سیستم های ماژول برای رفع این نیاز معرفی شدند و توسعه دهندگان را قادر می ساخت تا کد خود را به واحدهای قابل مدیریت و قابل استفاده مجدد تقسیم کنند.1.
ظهور CommonJS
CommonJS در سال 2009 با نام اصلی ServerJS تاسیس شد2. این برای جاوا اسکریپت سمت سرور طراحی شده است و قراردادهایی را برای تعریف ماژول ها ارائه می دهد. Node.js CommonJS را به عنوان سیستم ماژول پیشفرض خود پذیرفت و آن را در میان توسعهدهندگان جاوا اسکریپت رایج کرد. CommonJS استفاده می کند require
برای واردات و module.exports
برای صادرات ماژول ها تمام عملیات در CommonJS همزمان هستند، به این معنی که هر ماژول به صورت جداگانه بارگذاری می شود.
ظهور ESM (ماژول های ECMAScript)
در سال 2015، ECMAScript یک سیستم ماژول جدید به نام ECMAScript Modules (ESM) را معرفی کرد که در درجه اول توسعه سمت مشتری را هدف قرار می داد. ESM استفاده می کند import
و export
عبارات و عملیات آن ناهمزمان هستند و به ماژول ها اجازه می دهند به صورت موازی بارگذاری شوند3. در ابتدا، ESM برای مرورگرها در نظر گرفته شده بود، در حالی که CommonJS برای سرورها طراحی شده بود. روز به روز به استانداردی برای اکوسیستم JS تبدیل شد. امروزه، زمان اجراهای جاوا اسکریپت مدرن از هر دو سیستم ماژول پشتیبانی می کند. مرورگرها در سال 2017 شروع به پشتیبانی از ESM به صورت بومی کردند. حتی Typescript هم نحو ESM را تطبیق داد و هر زمان که آن را یاد گرفتید، ESM را نیز ناخودآگاه یاد میگیرید.
CommonJS اینجاست تا بماند
حقیقت این است که بستههای CommonJS (CJS) بسیار بیشتری نسبت به بستههای فقط ESM وجود دارد.4.
با این حال، یک روند واضح وجود دارد. تعداد بستههای فقط ESM یا دو ماژول در حال افزایش است، در حالی که بستههای فقط CJS در حال ایجاد هستند. این روند بر اولویت رو به رشد برای ESM تاکید می کند و این سوال را مطرح می کند که چه تعداد از بسته های فقط CJS به طور فعال نگهداری می شوند.
مقایسه
یک مقایسه جالب بین CommonJS و ESM شامل معیارهای عملکرد است. به دلیل ماهیت همزمان آن، CommonJS هنگام استفاده مستقیم از دستورات نیاز و واردات سریعتر است. بیایید مثال زیر را در نظر بگیریم:
// CommonJS -> s3-get-files.cjs
const s3 = require('@aws-sdk/client-s3');
new s3.S3Client({ region: 'eu-central-1' });
// ESM -> s3-get-files.mjs
import { S3Client } from '@aws-sdk/client-s3';
new S3Client({ region: 'eu-central-1' });
من از aws-sdk S3-Client استفاده کردم زیرا از ماژول دوگانه پشتیبانی می کند. در اینجا ما کلاینت را نمونه سازی می کنیم و سپس آن را با آن اجرا می کنیم node
:
hyperfine --warmup 10 --style color 'node s3-get-files.cjs' 'node s3-get-files.mjs'
Benchmark 1: node s3-get-files.cjs
Time (mean ± σ): 82.6 ms ± 3.7 ms [User: 78.5 ms, System: 16.7 ms]
Range (min … max): 78.0 ms … 93.6 ms 37 runs
Benchmark 2: node s3-get-files.mjs
Time (mean ± σ): 93.9 ms ± 4.0 ms [User: 98.3 ms, System: 18.1 ms]
Range (min … max): 88.1 ms … 104.8 ms 32 runs
Summary
node s3-get-files.cjs ran
1.14 ± 0.07 times faster than node s3-get-files.mjs
همانطور که می بینید، s3-get-files.cjs
و بنابراین CommonJS سریعتر اجرا می شود.
من از Buns Blogpost الهام گرفتم.
با این حال، زمانی که می خواهید کتابخانه JS خود را تولید کنید، باید آن را باندل کنید. در غیر این صورت، شما همه را ارسال خواهید کرد node_modules
. استفاده می شود esbuild
زیرا می تواند به CJS و ESM بسته شود. اکنون، بیایید همان بنچمارک را با نسخه همراه اجرا کنیم.
hyperfine --warmup 10 --style color 'node s3-bundle.cjs' 'node s3-bundle.mjs'
Benchmark 1: node s3-bundle.cjs
Time (mean ± σ): 62.1 ms ± 2.5 ms [User: 53.8 ms, System: 6.7 ms]
Range (min … max): 59.5 ms … 74.5 ms 45 runs
Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
Benchmark 2: node s3-bundle.mjs
Time (mean ± σ): 45.3 ms ± 2.2 ms [User: 38.1 ms, System: 5.6 ms]
Range (min … max): 43.0 ms … 59.2 ms 62 runs
Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
Summary
node s3-bundle.mjs ran
1.37 ± 0.09 times faster than node s3-bundle.cjs
همانطور که می بینید، s3-bundle.mjs
اکنون سریعتر از s3-bundle.cjs
. فایل ESM در حال حاضر حتی از فایل Unbundled CommonJS نیز سریعتر است، زیرا به دلیل تکان دادن درخت کارآمد – فرآیندی که کدهای استفاده نشده را حذف میکند، منجر به اندازههای فایل کوچکتر و زمان بارگذاری سریعتر میشود.
ESM را در آغوش بگیرید!
آینده ماژول های جاوا اسکریپت بدون شک به سمت ESM متمایل است. این با ایجاد یک پروژه جدید NodeJS یا حتی یک پروژه React شروع می شود. هر آموزش و مقاله ای از import
– بیانیه، که بنابراین ESM است. علیرغم بسیاری از بسته های CommonJS موجود، این روند در حال تغییر است زیرا توسعه دهندگان و نگهبانان بیشتری از ESM برای مزایای عملکرد و نحو مدرن آن استفاده می کنند. سوال دیگر همچنین این است که چه تعداد از این پروژه های فقط CJS هنوز حفظ می شوند.
ESM استانداردی است که در هر زمان اجرا مانند NodeJS، Bun یا Deno و در مرورگر بدون اجرا بر روی سرور کار می کند. تبدیل از طریق Babel به CommonJS ضروری نیست زیرا مرورگر ESM را درک می کند. همچنان میتوانید از Babel برای تبدیل به نسخه ECMAScript دیگری استفاده کنید، اما نباید به CJS تبدیل کنید.
شما باید فقط در ESM توسعه دهید زیرا هر زمان اجرا و مرورگر جدیدتر از 2017 ESM را درک می کند.
اگر کد شما خراب شود، ممکن است مشکلات قدیمی داشته باشید. استفاده از ابزار یا بسته های مختلف را در نظر بگیرید. به عنوان مثال، شما می توانید از Jest به vitest
یا از ExpressJS به h3
. نحو ثابت می ماند. تنها تفاوت در بیانیه واردات است.
خوراکی های کلیدی:
- بسته های کوچکتر: ESM از طریق تکان دادن درخت، بستههای کوچکتری تولید میکند که منجر به زمان بارگذاری سریعتر میشود.
- پشتیبانی جهانی: ESM به صورت بومی توسط مرورگرها و زمان های اجرا جاوا اسکریپت (Node.js، Bun، Deno) پشتیبانی می شود.
- اثبات آینده: با پذیرش مداوم، ESM به عنوان استاندارد برای ماژول های جاوا اسکریپت مدرن قرار می گیرد.
برای شروع، می توانید این Gist را دنبال کنید یا در اینجا یک یادگیری الهام بخش دریافت کنید.
برای آینده بهتر جاوا اسکریپت، ESM را در آغوش بگیرید!
ارائه
منابع بیشتر
-
https://www.freecodecamp.org/news/javascript-es-modules-and-module-bundlers/#why-use-modules ↩
-
https://deno.com/blog/commonjs-is-hurting-javascript ↩
-
https://tc39.es/ecma262/#sec-overview ↩