Skip to content

핫 모듈 교체 (HMR) 는 전체 페이지 새로고침 없이 실행 중인 애플리케이션에서 모듈을 업데이트할 수 있게 해줍니다. 이는 애플리케이션 상태를 유지하고 개발 경험을 개선합니다.

NOTE

HMR 은 Bun 의 풀스택 개발 서버를 사용할 때 기본적으로 활성화됩니다.

import.meta.hot API 참조

Bun 은 Vite 의 import.meta.hot API 를 모델로 한 클라이언트 측 HMR API 를 구현합니다. if (import.meta.hot) 로 확인하여 프로덕션에서 트리 쉐이킹할 수 있습니다.

ts
if (import.meta.hot) {
  // HMR API 를 사용할 수 있습니다.
}

하지만 Bun 은 프로덕션 빌드에서 모든 HMR API 호출을 데드 코드 제거하므로 이 확인은 종종 필요하지 않습니다.

ts
// 이 전체 함수 호출은 프로덕션에서 제거됩니다!
import.meta.hot.dispose(() => {
  console.log("dispose");
});

NOTE

HMR API 는 아직 진행 중인 작업입니다. 일부 기능은 누락되었습니다. HMR 은 `Bun.serve` 에서 development 옵션을 `{ hmr: false }` 로 설정하여 비활성화할 수 있습니다.

API 메서드

메서드상태참고
hot.accept()핫 업데이트를 부드럽게 교체할 수 있음을 나타냄.
hot.data모듈 평가 간에 데이터 유지.
hot.dispose()모듈이 교체되기 직전에 실행될 콜백 함수 추가.
hot.invalidate()
hot.on()이벤트 리스너 첨부
hot.off()on 에서 이벤트 리스너 제거.
hot.send()
hot.prune()🚧참고: 콜백이 현재 호출되지 않음.
hot.decline()Vite 의 import.meta.hot 과 일치하도록 no-op

import.meta.hot.accept()

accept() 메서드는 모듈이 핫 교체될 수 있음을 나타냅니다. 인수 없이 호출되면 이 모듈은 파일을 다시 평가하여 교체될 수 있음을 나타냅니다. 핫 업데이트 후 이 모듈의 임포터는 자동으로 패치됩니다.

ts
// index.ts
import { getCount } from "./foo.ts";

console.log("count is ", getCount());

import.meta.hot.accept();

export function getNegativeCount() {
  return -getCount();
}

이것은 index.ts 가 가져오는 모든 파일에 대한 핫 리로딩 경계를 생성합니다. 즉, foo.ts 또는 해당 종속성이 저장될 때마다 업데이트가 index.ts 까지 버블링되어 다시 평가됩니다. index.ts 를 가져오는 파일은 새로운 getNegativeCount() 버전을 가져오도록 패치됩니다. index.ts 만 업데이트되면 하나의 파일만 다시 평가되고 foo.ts 의 카운터는 재사용됩니다.

이는 import.meta.hot.data 와 결합하여 이전 모듈에서 새 모듈로 상태를 전송하는 데 사용될 수 있습니다.

콜백과 함께

하나의 콜백을 제공하면 import.meta.hot.accept 는 Vite 에서 작동하는 방식으로 작동합니다. 이 모듈의 임포터를 패치하는 대신 새 모듈과 함께 콜백을 호출합니다.

ts
export const count = 0;

import.meta.hot.accept(newModule => {
  if (newModule) {
    // newModule 은 SyntaxError 가 발생했을 때 undefined 입니다
    console.log("업데이트됨: count 는 이제 ", newModule.count);
  }
});

다른 모듈 허용

ts
import { count } from "./foo";

import.meta.hot.accept("./foo", () => {
  if (!newModule) return;

  console.log("업데이트됨: count 는 이제 ", count);
});

종속성의 모듈을 허용할 수 있음을 나타냅니다. 종속성이 업데이트되면 새 모듈과 함께 콜백이 호출됩니다.

여러 종속성과 함께

ts
import.meta.hot.accept(["./foo", "./bar"], newModules => {
  // newModules 는 각 항목이 업데이트된 모듈에 해당하는 배열입니다
  // 또는 해당 모듈에 오류가 있으면 undefined
});

여러 종속성의 모듈을 허용할 수 있음을 나타냅니다. 이 변형은 종속성 배열을 허용하며 콜백은 업데이트된 모듈과 오류가 있는 경우 undefined 를 받습니다.

import.meta.hot.data

import.meta.hot.data 는 핫 교체 중 모듈 인스턴스 간에 상태를 유지하여 이전 버전에서 새 버전으로 데이터 전송을 가능하게 합니다. import.meta.hot.data 에 쓰기가 발생하면 Bun 은 이 모듈이 자체 허용 가능한 것으로 표시합니다 (import.meta.hot.accept() 호출과 동일).

tsx
import { createRoot } from "react-dom/client";
import { App } from "./app";

const root = (import.meta.hot.data.root ??= createRoot(elem));
root.render(<App />); // 기존 root 재사용

프로덕션에서 data{} 로 인라인되므로 상태 홀더로 사용할 수 없습니다.

import.meta.hot.dispose()

on-dispose 콜백을 첨부합니다. 이는 다음 시점에 호출됩니다:

  • 모듈이 다른 사본으로 교체되기 직전 (다음이 로드되기 전)
  • 모듈이 분리된 후 (이 모듈에 대한 모든 가져오기 제거, import.meta.hot.prune() 참조)
ts
const sideEffect = setupSideEffect();

import.meta.hot.dispose(() => {
  sideEffect.cleanup();
});

promise 를 반환하면 모듈이 처리될 때까지 모듈 교체가 지연됩니다. 모든 dispose 콜백은 병렬로 호출됩니다.

import.meta.hot.prune()

on-prune 콜백을 첨부합니다. 이는 이 모듈에 대한 모든 가져오기가 제거되었지만 모듈이 이전에 로드되었을 때 호출됩니다.

이는 모듈이 로드될 때 생성된 리소스를 정리하는 데 사용할 수 있습니다. import.meta.hot.dispose() 와 달리 acceptdata 와 더 잘 쌍을 이루어 상태가 있는 리소스를 관리합니다. WebSocket 을 관리하는 전체 예제:

ts
import { something } from "./something";

// WebSocket 연결 초기화 또는 재사용
export const ws = (import.meta.hot.data.ws ??= new WebSocket(location.origin));

// 모듈의 가져오기가 제거되면 WebSocket 연결을 정리합니다.
import.meta.hot.prune(() => {
  ws.close();
});

import.meta.hot.on() 및 off()

on()off() 는 HMR 런타임에서 이벤트를 수신하는 데 사용됩니다. 이벤트 이름은 플러그인이 서로 충돌하지 않도록 접두사가 붙습니다.

ts
import.meta.hot.on("bun:beforeUpdate", () => {
  console.log("핫 업데이트 전");
});

파일이 교체되면 모든 이벤트 리스너가 자동으로 제거됩니다.

내장 이벤트

이벤트발생 시기
bun:beforeUpdate핫 업데이트가 적용되기 전.
bun:afterUpdate핫 업데이트가 적용된 후.
bun:beforeFullReload전체 페이지 새로고침이 발생하기 전.
bun:beforePruneprune 콜백이 호출되기 전.
bun:invalidate모듈이 import.meta.hot.invalidate() 로 무효화될 때
bun:error빌드 또는 런타임 오류가 발생할 때
bun:ws:disconnectHMR WebSocket 연결이 끊어졌을 때. 개발 서버가 오프라인일 수 있음을 나타냄.
bun:ws:connectHMR WebSocket 이 연결되거나 재연결될 때.

NOTE

Vite 와의 호환성을 위해 위의 이벤트는 `bun:*` 접두사 대신 `vite:*` 접두사로도 사용할 수 있습니다.

Bun by www.bunjs.com.cn 편집