Замена модулей при запуске
«Замена модулей при запуске» (HMR) — это функция, позволяющая встраивать обновленные модули в активную среду выполнения.
Это как LiveReload для каждого модуля.
HMR — это «опциональная» функция, поэтому вам необходимо добавить код в определённые точки вашего приложения. Зависимости обрабатываются системой модулей.
Например, вы размещаете свой код замены в модуле A. Модуль A использует модуль B, а B использует C. Если модуль C обновлен, а модуль B не может обработать обновление, модули B и C становятся устаревшими. Модуль A может обработать обновление, и новые модули B и C будут вставлены.
Примеры
Пример 1: замена обработчика запросов HTTP-сервера
var requestHandler = require("./handler.js");
var server = require("http").createServer();
server.on("request", requestHandler);
server.listen(8080);
// check if HMR is enabled
if(module.hot) {
// accept update of dependency
module.hot.accept("./handler.js", function() {
// replace request handler of server
server.removeListener("request", requestHandler);
requestHandler = require("./handler.js");
server.on("request", requestHandler);
});
}
Пример 2: замена CSS
// addStyleTag(css: string) => HTMLStyleElement
var addStyleTag = require("./addStyleTag");
var element = addStyleTag(".rule { attr: name }");
module.exports = null;
// check if HMR is enabled
if(module.hot) {
// accept itself
module.hot.accept();
// removeStyleTag(element: HTMLStyleElement) => void
var removeStyleTag = require("./removeStyleTag");
// dispose handler
module.hot.dispose(function() {
// revoke the side effect
removeStyleTag(element);
});
}
Пример 3: Замена модулей с помощью require.context
var context = require.context("./filesToLoad", false, /\.js$/); //filesToLoad is a directory with .js files
var modules = {};
context.keys().forEach(function (key) {
var module = context(key);
modules[key] = module;
customReloadLogic(key, module, false);
})
if (module.hot) {
module.hot.accept(context.id, function () {
//You can't use context here. You _need_ to call require.context again to
//get the new version. Otherwise you might get errors about using disposed
//modules
var reloadedContext = require.context("./filesToLoad", false, /\.js$/);
//To find out what module was changed you just compare the result of the
//require call with the version stored in the modules hash using strict
//equality. Equal means it is unchanged.
var changedModules = reloadedContext.keys()
.map(function (key) {
return [key, reloadedContext(key)];
})
.filter(function (reloadedModule) {
return modules[reloadedModule[0]] !== reloadedModule[1];
});
changedModules.forEach(function (module) {
modules[module[0]] = module[1];
customReloadLogic(module[0], module[1], true);
});
});
}
function customReloadLogic(name, module, isReload) {
console.log("module " + name + (isReload ? " re" : " ") + "loaded");
}
(см. https://github.com/jauco/webpack-hot-module-reload-with-context-example для полной рабочей версии)
API
Если HMR включен для модуля module.hot — это объект, содержащий следующие свойства:
accept
accept(dependencies: string[], callback: (updatedDependencies) => void) => void accept(dependency: string, callback: () => void) => void
Принимает обновления кода для указанных зависимостей. Обратный вызов вызывается, когда зависимости были заменены.
accept([errHandler]) => void
Принимает обновления кода для данного модуля без уведомления родителей. Это следует использовать только в том случае, если модуль ничего не экспортирует. errHandler может использоваться для обработки ошибок, возникающих при загрузке обновленного модуля.
decline
decline(dependencies: string[]) => void decline(dependency: string) => void
Не принимать обновления для указанных зависимостей. Если какая-либо зависимость обновлена, обновление кода завершится с кодом "decline".
decline() => void
Отметить текущий модуль как не подлежащий обновлению. Если он обновлен, код обновления завершится с кодом "decline".
dispose/addDisposeHandler
dispose(callback: (data: object) => void) => void addDisposeHandler(callback: (data: object) => void) => void
Добавляет обработчик, который выполняется при замене текущего кода модуля. Здесь вы должны уничтожить/удалить любые постоянные ресурсы, которые вы затребовали/создали. Если вы хотите передать состояние новому модулю, добавьте его в объект data. Объект data будет доступен в module.hot.data нового модуля.
removeDisposeHandler
removeDisposeHandler(callback: (data: object) => void) => void
Удалить обработчик.
Это может быть полезно для добавления временного обработчика удаления. Вы можете, например, заменить код во время выполнения многоэтапной асинхронной функции.
API управления
Также в объекте module.hot.
check
check([autoApply], callback: (err: Error, outdatedModules: Module[]) => void
Выбрасывает исключение, если status() не idle.
Проверяет все загруженные модули на наличие обновлений и применяет их, если они найдены.
Если обновление не найдено, обратный вызов вызывается с null.
Если autoApply истинно, обратный вызов будет вызван со всеми модулями, которые были удалены. apply() автоматически вызывается с autoApply в качестве параметра options.
Если autoApply не задан, обратный вызов будет вызван со всеми модулями, которые будут удалены при apply().
apply
apply([options], callback: (err: Error, outdatedModules: Module[]) => void
Если status() != "ready" , он генерирует ошибку.
Продолжить процесс обновления.
options может быть объектом, содержащим следующие параметры:
-
ignoreUnaccepted: Если значение истинно, процесс обновления продолжается даже если некоторые модули не приняты (и будут переданы в точку входа).
status
status() => string
Возвращает одно из значений idle, check, watch, watch-delay, prepare, ready, dispose, apply, abort или fail.
idle
HMR ожидает вашего вызова check(). Когда вы его вызовете, состояние изменится на check.
check
HMR проверяет наличие обновлений. Если обновлений нет, он вернётся в состояние idle.
Если обновления найдены, он пройдет этапы prepare, dispose и apply. Затем вернётся в состояние idle.
watch
HMR находится в режиме слежения и автоматически будет уведомлен о изменениях. После первого изменения он перейдет в состояние watch-delay и подождет определенное время, чтобы начать процесс обновления. Любое изменение сбросит таймер, чтобы накопить больше изменений. Когда начнется процесс обновления, он пройдет этапы prepare, dispose и apply. Затем вернётся в состояние watch или watch-delay , если во время обновления были обнаружены изменения.
prepare
HMR готовит данные для обновления. Это может означать, что он что-то загружает.
ready
Обновление доступно и подготовлено. Вызовите apply() для продолжения.
dispose
HMR вызывает обработчики удаления модулей, которые будут заменены.
apply
HMR вызывает обработчики принятия родителей заменённых модулей, затем он требует самопринятые модули.
abort
Обновление не может быть применено, но система всё ещё в (старом) согласованном состоянии.
fail
Обновление выбросило исключение в процессе, и система может быть (возможно) в несогласованном состоянии. Система должна быть перезапущена.
status/addStatusHandler
status(callback: (status: string) => void) => void addStatusHandler(callback: (status: string) => void) => void
Регистрирует обратный вызов при изменении статуса.
removeStatusHandler
removeStatusHandler(callback: (status: string) => void) => void
Удаляет зарегистрированный обработчик изменений состояния.
Как справиться с…
… модулем без побочных эффектов (стандартный случай)
В модуле ничего не нужно делать. Любой родитель может его принять.
… модулем с побочными эффектами
Модулю нужен обработчик удаления, затем любой родитель может его принять.
… модулем только с побочными эффектами и без экспортов
Модулю нужен обработчик удаления, и он может принять себя. Действия со стороны родителя не требуются.
Если код модуля вам не принадлежит, родитель может принять модуль с некоторой пользовательской логикой удаления.
… модулем входа в приложение
Поскольку он ничего не экспортирует, он может принять себя. Обработчик удаления может передать состояние приложения при замене.
… внешним модулем с необрабатываемыми побочными эффектами
В ближайшем родителе вы отклоняете зависимость. Это заставит ваше приложение выдавать ошибку при обновлении. Но так как это внешний модуль, обновление происходит очень редко.
© 2012–2015 Tobias Koppers
Licensed under the MIT License.
https://webpack.github.io/docs/hot-module-replacement.html