>  기사  >  웹 프론트엔드  >  오 CommonJS! 왜 나랑 문자하는 거야?! CommonJS를 버리는 이유

오 CommonJS! 왜 나랑 문자하는 거야?! CommonJS를 버리는 이유

PHPz
PHPz원래의
2024-07-17 21:56:18702검색

평소의 패치일이었습니다. 코드를 변경하지 않고 npm 종속성을 패치하고 업그레이드했는데 갑자기 일부 단위 테스트가 실패했습니다.
Oh CommonJS! Why are you mESMing with me?! Reasons to ditch CommonJS

이런!

Oh CommonJS! Why are you mESMing with me?! Reasons to ditch CommonJS

Jest가 예상치 못한 토큰을 발견했기 때문에 테스트가 실패했습니다. Jest가 ESM 전용 패키지를 기본적으로 처리할 수 없기 때문에 실패했습니다. 실제로 Jest는 CommonJS로 작성되었습니다.
그런데 그게 무슨 뜻인가요? 그러기 위해서는 CommonJS와 ESM이 존재하는 이유를 이해해야 합니다.

모듈 시스템이 필요한 이유는 무엇입니까?

웹 개발 초기에는 JavaScript가 주로 jQuery와 같은 라이브러리를 사용하여 DOM(문서 개체 모델)을 조작하는 데 사용되었습니다. 그러나 Node.js의 도입으로 인해 서버 측 프로그래밍에도 JavaScript가 사용되었습니다. 이러한 변화로 인해 JavaScript 코드베이스의 복잡성과 크기가 증가했습니다. 이에 따라 자바스크립트 코드를 체계적으로 정리하고 관리할 수 있는 구조화된 방법이 필요하게 되었습니다. 이러한 요구를 충족하기 위해 모듈 시스템이 도입되어 개발자는 코드를 관리 가능하고 재사용 가능한 단위로 나눌 수 있습니다1.

CommonJS의 출현

CommonJS는 2009년에 설립되었으며 원래 이름은 ServerJS2였습니다. 이는 서버 측 JavaScript용으로 설계되었으며 모듈 정의에 대한 규칙을 제공합니다. Node.js는 CommonJS를 기본 모듈 시스템으로 채택하여 백엔드 JavaScript 개발자들 사이에서 널리 보급되었습니다. CommonJS는 모듈을 가져오기 위해 require를 사용하고 모듈을 내보내기 위해 module.exports를 사용합니다. CommonJS의 모든 작업은 동기식입니다. 즉, 각 모듈이 개별적으로 로드됩니다.

ESM(ECMAScript 모듈)의 부상

2015년 ECMAScript는 주로 클라이언트 측 개발을 목표로 하는 ECMAScript 모듈(ESM)이라는 새로운 모듈 시스템을 도입했습니다. ESM은 가져오기 및 내보내기 문을 사용하며 해당 작업은 비동기식이므로 모듈을 병렬로 로드할 수 있습니다3. 처음에 ESM은 브라우저용으로 설계된 반면 CommonJS는 서버용으로 설계되었습니다. 이는 점점 더 JS 생태계의 표준이 되었습니다. 요즘 최신 JavaScript 런타임은 두 모듈 시스템을 모두 지원합니다. 브라우저는 2017년부터 기본적으로 ESM을 지원하기 시작했습니다. Typescript조차도 ESM 구문을 채택했으며, 배울 때마다 무의식적으로 ESM을 배우게 됩니다.

How Are you not dead.jpg

CommonJS는 여기에 있습니다

사실 ESM 전용 패키지보다 CommonJS(CJS) 전용 패키지가 더 많습니다4.
Oh CommonJS! Why are you mESMing with me?! Reasons to ditch CommonJS
그러나 분명한 추세가 있습니다. ESM 전용 또는 이중 모듈 패키지의 수가 증가하는 반면 CJS 전용 패키지는 점점 줄어들고 있습니다. 이러한 추세는 ESM에 대한 선호도가 높아지고 있음을 강조하며 얼마나 많은 CJS 전용 패키지가 적극적으로 유지 관리되고 있는지에 대한 의문을 제기합니다.

비교

CommonJS와 ESM의 흥미로운 비교에는 성능 벤치마크가 포함됩니다. 동기식 특성으로 인해 CommonJS는 require 및 import 문을 직접 사용할 때 더 빠릅니다. 다음 예를 살펴보겠습니다.

// 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_module을 배송하게 됩니다. CJS, ESM에 번들로 묶을 수 있기 때문에 esbuild를 사용합니다. 이제 번들 버전으로 동일한 벤치마크를 실행해 보겠습니다.

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 파일은 사용되지 않는 코드를 제거하는 프로세스인 효율적인 트리 쉐이킹으로 인해 파일 크기가 더 작아지고 로드 시간이 빨라지기 때문에 번들로 제공되지 않은 CommonJS 파일보다 훨씬 빠릅니다.

ESM을 받아들여라!

JavaScript 모듈의 미래는 의심할 여지 없이 ESM 쪽으로 기울고 있습니다. 이는 새로운 NodeJS 프로젝트 또는 심지어 React 프로젝트를 생성할 때 시작됩니다. 모든 튜토리얼과 기사는 import-문, 즉 ESM을 사용합니다. 기존의 많은 CommonJS 패키지에도 불구하고 더 많은 개발자와 유지관리자가 성능 이점과 최신 구문을 위해 ESM을 채택함에 따라 추세가 바뀌고 있습니다. 또 다른 질문은 이러한 CJS 전용 프로젝트 중 얼마나 많은 것이 아직도 유지되고 있느냐는 것입니다.

ESM은 NodeJS, Bun, Deno 등 모든 런타임과 서버에서 실행되지 않고 브라우저에서 작동하는 표준입니다. 브라우저는 ESM을 이해하므로 Babel을 통해 CommonJS로 변환할 필요가 없습니다. Babel을 사용하여 다른 ECMAScript 버전으로 변환할 수 있지만 CJS로 변환하면 안 됩니다.

현재 모든 런타임과 2017년 이후의 브라우저는 ESM을 이해하므로 ESM 전용으로 개발해야 합니다.

코드가 손상되면 레거시 문제가 있을 수 있습니다. 다른 도구나 패키지를 사용해 보세요. 예를 들어 Jest에서 vitest로 또는 ExpressJS에서 h3으로 마이그레이션할 수 있습니다. 구문은 동일하게 유지됩니다. 유일한 차이점은 import 문입니다.

주요 시사점:

  • 작은 번들: ESM은 트리 쉐이킹을 통해 더 작은 번들을 생성하므로 로드 시간이 더 빨라집니다.
  • 범용 지원: ESM은 기본적으로 브라우저 및 JavaScript 런타임(Node.js, Bun, Deno)에서 지원됩니다.
  • 미래 보장: ESM은 지속적인 채택을 통해 최신 JavaScript 모듈의 표준으로 자리매김하고 있습니다.

시작하려면 이 Gist를 따르거나 여기에서 영감을 얻을 수 있습니다.

더 나은 JavaScript 미래를 위해 ESM을 수용하세요!

발표회

추가 리소스

  • https://dev.to/logto/ migration-a-60k-loc-typescript-nodejs-repo-to-esm-and-testing-become-4x-faster-22-4a4k
  • https://jakearchibald.com/2017/es-modules-in-browsers/
  • https://gist.github.com/joepie91/bca2fda868c1e8b2c2caf76af7dfcad3
  • https://gist.github.com/joepie91/bca2fda868c1e8b2c2caf76af7dfcad3

  1. https://www.freecodecamp.org/news/javascript-es-modules-and-module-bundlers/#why-use-modules ↩

  2. https://deno.com/blog/commonjs-is-hurting-javascript ↩

  3. https://tc39.es/ecma262/#sec-overview ↩

  4. https://twitter.com/wooorm/status/1759918205928194443 ↩

위 내용은 오 CommonJS! 왜 나랑 문자하는 거야?! CommonJS를 버리는 이유의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.