استبدال الوحدة الساخنة (HMR) يسمح لك بتحديث الوحدات في تطبيق قيد التشغيل دون الحاجة لإعادة تحميل الصفحة بالكامل. هذا يحافظ على حالة التطبيق ويحسن تجربة التطوير.
NOTE
HMR مفعل افتراضيًا عند استخدام خادم تطوير Bun كامل المكدس.مرجع واجهة برمجة تطبيقات import.meta.hot
Bun ينفذ واجهة برمجة تطبيقات HMR من جانب العميل على غرار واجهة برمجة تطبيقات import.meta.hot في Vite. يمكن التحقق منها بـ if (import.meta.hot)، مما يسمح بإزالتها في الإنتاج.
if (import.meta.hot) {
// واجهات برمجة تطبيقات HMR متاحة.
}لكن، هذا التحقق غالبًا غير ضروري لأن Bun سيزيل الكود الميت لاستدعاءات جميع واجهات برمجة تطبيقات HMR في بناء الإنتاج.
// سيتم إزالة استدعاء الدالة هذا بالكامل في الإنتاج!
import.meta.hot.dispose(() => {
console.log("dispose");
});NOTE
واجهة برمجة تطبيقات HMR لا تزال قيد التطوير. بعض الميزات مفقودة. يمكن تعطيل 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() | ✅ | لا شيء لمطابقة import.meta.hot في Vite |
import.meta.hot.accept()
طريقة accept() تشير إلى أنه يمكن استبدال الوحدة ساخنًا. عند استدعائها بدون وسائط، تشير إلى أنه يمكن استبدال هذه الوحدة ببساطة عن طريق إعادة تقييم الملف. بعد تحديث ساخن، سيتم تصحيح مستوردي هذه الوحدة تلقائيًا.
// 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. بدلاً من تصحيح مستوردي هذه الوحدة، ستستدعي استدعاء回调 مع الوحدة الجديدة.
export const count = 0;
import.meta.hot.accept(newModule => {
if (newModule) {
// newModule غير معرّف عند حدوث SyntaxError
console.log("updated: count is now ", newModule.count);
}
});قبول وحدات أخرى
import { count } from "./foo";
import.meta.hot.accept("./foo", () => {
if (!newModule) return;
console.log("updated: count is now ", count);
});تشير إلى أنه يمكن قبول وحدة التبعية. عند تحديث التبعية، سيتم استدعاء استدعاء回调 مع الوحدة الجديدة.
مع تبعيات متعددة
import.meta.hot.accept(["./foo", "./bar"], newModules => {
// newModules مصفوفة حيث كل عنصر يتوافق مع الوحدة المحدثة
// أو غير معرّف إذا كان لدى تلك الوحدة خطأ في الصيغة
});تشير إلى أنه يمكن قبول وحدات تبعيات متعددة. هذا البديل يقبل مصفوفة من التبعيات، حيث سيستقبل استدعاء回调 الوحدات المحدثة، و undefined لأي كان لديها أخطاء.
import.meta.hot.data
import.meta.hot.data يحافظ على الحالة بين مثيلات الوحدة أثناء الاستبدال الساخن، مما يمكّن نقل البيانات من الإصدارات السابقة إلى الجديدة. عند الكتابة في import.meta.hot.data، سيقوم Bun أيضًا بتمييز هذه الوحدة كقادرة على القبول الذاتي (مكافئ لاستدعاء import.meta.hot.accept()).
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()
يرفق استدعاء回调 عند التخلص. يُستدعى هذا:
- قبل استبدال الوحدة بنسخة أخرى مباشرة (قبل تحميل التالية)
- بعد فصل الوحدة (إزالة جميع الواردات إلى هذه الوحدة، راجع
import.meta.hot.prune())
const sideEffect = setupSideEffect();
import.meta.hot.dispose(() => {
sideEffect.cleanup();
});إرجاع promise سيؤخر استبدال الوحدة حتى يتم التخلص من الوحدة. جميع استدعاءات回调 dispose تُستدعى بالتوازي.
import.meta.hot.prune()
يرفق استدعاء回调 عند التقليم. يُستدعى هذا عند إزالة جميع الواردات إلى هذه الوحدة، لكن الوحدة تم تحميلها سابقًا.
يمكن استخدام هذا لتنظيف الموارد التي تم إنشاؤها عند تحميل الوحدة. على عكس import.meta.hot.dispose()، هذا يقترن بشكل أفضل مع accept و data لإدارة الموارد ذات الحالة. مثال كامل لإدارة WebSocket:
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. أسماء الأحداث مسبوقة ببادئة حتى لا تتعارض المكونات الإضافية مع بعضها البعض.
import.meta.hot.on("bun:beforeUpdate", () => {
console.log("before a hot update");
});عند استبدال ملف، تتم إزالة جميع مستمعي الأحداث تلقائيًا.
الأحداث المدمجة
| الحدث | يُصدر عندما |
|---|---|
bun:beforeUpdate | قبل تطبيق تحديث ساخن. |
bun:afterUpdate | بعد تطبيق تحديث ساخن. |
bun:beforeFullReload | قبل حدوث إعادة تحميل صفحة كاملة. |
bun:beforePrune | قبل استدعاء استدعاءات回调 التقليم. |
bun:invalidate | عند إبطال وحدة مع import.meta.hot.invalidate() |
bun:error | عند حدوث خطأ في البناء أو وقت التشغيل |
bun:ws:disconnect | عند فقدان اتصال HMR WebSocket. هذا يمكن أن يشير إلى أن خادم التطوير غير متصل. |
bun:ws:connect | عند اتصال أو إعادة اتصال HMR WebSocket. |
NOTE
للتوافق مع Vite، الأحداث أعلاه متاحة أيضًا عبر البادئة `vite:*` بدلاً من `bun:*`.