es-modules
- es모듈은 자바스크립트의 공식적이고 표준과 된 모듈 시스템이 됨
모듈은 왜 쓰는가
- 함수 간 변수 공유를 위해 전역과 같이 상위 스코프에 배치하면 의존성이 생겨서 어디서 사용하는지 추적이 어렵고 유지보수하기 힘듬
- 모듈은 관련있는 함수와 변수를 모아놓아 모듈 스코프를 통해 모듈내에서 공유가 가능하고 외부에서 접근할 수 있도록 export를 해주어 의존성을 명시할 수 있음
- 모듈 시스템에는 nodejs에서 쓰는 commonjs와 자바스크립스 명세에 있는 EcmaScript modules이 있다.- AMD, UMD도 있음 https://www.zerocho.com/category/JavaScript/post/5b67e7847bbbd3001b43fd73
es 모듈 동작 방식
- 구성
- 사용자가 지정한 entry point부터 import 문을 찾아가면서 모든 파일을 다운로드하고 모듈 레코드로 구문 분석을 한다.
- 모듈 레코드는 실제로 파일 안에서 어떻게 동작하는지 명세하는 것
- 인스턴스화
- 모듈 인스턴스는 코드와 상태로 결합되어 있다. 상태는 현재 시점의 실제 변수값으로 코드에게 원료로 작용한다.
- 모듈들을 담을 메모리 공간을 찾아서 서로를 가리키도록 링크를 건다, 할당은 아직 안함
- 평가
- 코드를 실행하며 메모리에 변수들을 할당
es모듈은 구성-인스턴스화-평가 단계를 독립적으로 비동기로 수행된다.
CommonJS는 이 단계를 한꺼번에 이루어 져서 es모듈과 차이점이 있다.
es모듈의 단계가 실제로 반드시 비동기로 실행되는건 아니다. es모듈 명세는 비동기 적이여도 파일을 불러오는건 loader가 하기 때문에 플랫폼 별 로더 명세에 따라서 실행은 달라질 수 있다, 브라우저의 경우 HTML명세를 따름
로더는 모듈을 어떻게 불러 올지 제어한다. (es모듈 메소드)
구성
로더 동작 과정
- module resolution, 어디서 모듈을 다운로드 할 지 확인.
- URL, 파일시스템에서 파일을 다운로드해 가져온다.
- 파일을 모듈 레코드로 구문 분석.
로더는 스크립트 태그롤 사용해 entry point를 찾고 import문, 모듈 지정자로 따라간다.
플랫폼 별로 import문을 해석하는 알고리즘(module resolution)이 다르기 때문에 주의해야함.
엔트리부터 파일을 가져와서 구문 분석을 해 다음 의존성을 알아냄
메인 스레드가 파일을 각각 다운로드 할 때 까지 대기한다면 작업 대기열을 많이 쌓일 것이다. 이것이 브라우저에서 다운 로드 시간이 가장 긴 이유이다.
es모듈와 CommonJS의 차이점
- es모듈 명세가 알고리즘은 각 단계별로 나눠놓은 이유로 CommonJS와 의 주요 차이점이다.
- CommonJS는 파일 시스템에서 파일을 로드해서 인터넷으로 파일을 다운로드 하는거 보다 시간이 적지만 노드는 파일 다운로드 하는 동안 주 스레드가 차단된다.
- node에서는 현재 모듈이 이미 평가까지 다 되어 있기 때문에 모듈 지정자에 변수를 사용 할 수 있지만, es모듈은 비동기적으로 실행돼서 변수에 아직 할당이 안돼서 모듈 지정자(module resolution, import문)에서 변수를 못쓴다.
- es모듈에서는 이를 위해서 동적 import 기능이 있다.
es모듈의 동적 import
- 동적으로 import 한 모듈은 독립적으로 분리된 별개의 그래프로 취급 된다. 새로운 그래프로 시작함.
- 로더는 모듈 인스턴스를 캐시 하기 때문에 분리된 그래프여도 같은 모듈을 사용하고 있다면 공유된다, 엔진의 작업을 줄여줌
로더의 캐시
- 로더는 모듈맵으로 캐시를 관리한다.
- 로더가 URL를 가져올 때 모듈맵에 넣고 상태를 fetching으로 바꾸고 다음 파일은 가져온다.
- 다른 파일에서 같은 모듈을 만나면 모듈맵에서 URL을 검색하고 있으면 스킵함
파싱
- 파일을 불러온 후 모듈 레코드로 해석하고 모듈레코드를 모듈맵에 추가한다.
- 로더는 모듈맵에서 모듈을 가져옴.
- 스크립트 태그에 type="module"을 지정해서 모듈을 목표로 구문분석이 되어야 한다.
인스턴스화
- 인스턴스는 코드와 상태를 결합하며 메모리에 있는 상태들을 연결하는 것이다.
- js엔진은 한 모듈에 관한 모듈 환경 레코드를 생성해 export에 대한 메모리 상자를 찾아 모듈 환경 레코드에 링크를 시킨다.
- 메모리는 값이 할당 된 상태는 아니고 평가때 값이 채워진다. export된 함수 선언은 이 단계에서 초기화 됨.
- 모듈 그래프롤 인스턴스화 하기 위해서는 엔진은 깊이 우선 순회로 맨 아래까지, 의존성이 없는 모듈까지 조사 한다음에 export를 설정 한다.
- export를 먼저 연결하는 방식
commonjs차이
- commonjs는 export객체를 내보낼 때 복사를 하기 때문에 export하는 모듈에서 값변경이 일어나면 import에서 변경사항을 알지 못함
- es모듈은 라이브 바인딩으로 두 모듈이 같은 메모리를 보고 있기 때문에 싱크가 맞는다.
- export한 모듈은 값을 업데이트 할 수 있지만 import쪽은 변경 할 수 없다. 객체 자체를 내보내고 있으면 가능.
- 라이브 바인딩은 코드를 실행하지 않고도 연결을 할 수 있다.
평가
- js엔진은 최상위 레벨 코드를 실행하며 메모리에 할당을 한다..
- 인스턴스화에서 생성된 연결은 여러번 수행되어도 같지만 평가는 실행 될때 마다 다른 결과를 가질 수 있다. 예를들어 서버에서 뭔가 가져와서 값을 할당
- 모듈맵은 각 모듈에 대해 1개의 모듈 레코드를 가지고 한번만 실행하고 캐싱을 한다.
순환 문제
- 순환이 있는 의존성은 그래프에 루프가 생긴다.
- commonjs에서는 순차적으로 값을 복사해서 평가하기 때문에 순환 참조가 되면 할당 안된 값은 그대로 undefined로 남아 있음.
- es모듈에서는 라이브 바인딩으로 서로 연결되어 있어 순환 참조 되어도 올바를 값으로 할당이 된다.