Замена модулей в режиме реального времени
Это руководство дополняет примеры кода, приведенные в руководстве по разработке.
Замена модулей в режиме реального времени (или HMR) — одна из самых полезных функций webpack. Она позволяет обновлять модули в процессе работы без полной перезагрузки страницы. Эта страница фокусируется на реализации, в то время как на странице концепций приводятся более подробные сведения о том, как это работает и почему это полезно.
HMR не предназначен для использования в производственной среде, то есть его следует применять только в режиме разработки. Более подробную информацию см. в руководстве по сборке для производства.
Включение HMR
Эта функция полезна для повышения продуктивности. Все, что нам нужно сделать, это обновить конфигурацию нашего webpack-dev-server и использовать встроенный плагин HMR webpack. Также мы удалим точку входа для print.js , так как теперь она будет использоваться модулем index.js.
Если вы использовали
webpack-dev-middlewareвместоwebpack-dev-server, используйте пакетwebpack-hot-middlewareдля включения HMR на вашем собственном сервере или приложении.
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
- print: './src/print.js',
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
+ hot: true,
},
plugins: [
// new CleanWebpackPlugin(['dist/*']) for < v2 versions of CleanWebpackPlugin
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Hot Module Replacement',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
}; Вы можете использовать командную строку для изменения конфигурации webpack-dev-server с помощью следующей команды:
webpack-dev-server --hotOnly.
Теперь обновим файл index.js , чтобы при обнаружении изменений в print.js сообщить webpack о принятии обновленного модуля.
index.js
import _ from 'lodash';
import printMe from './print.js';
function component() {
const element = document.createElement('div');
const btn = document.createElement('button');
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
btn.innerHTML = 'Click me and check the console!';
btn.onclick = printMe;
element.appendChild(btn);
return element;
}
document.body.appendChild(component());
+
+ if (module.hot) {
+ module.hot.accept('./print.js', function() {
+ console.log('Accepting the updated printMe module!');
+ printMe();
+ })
+ } Начните изменять оператор console.log в print.js, и вы должны увидеть следующий вывод в консоли браузера (не беспокойтесь о выводе button.onclick = printMe пока, мы также обновим эту часть позже).
print.js
export default function printMe() {
- console.log('I get called from print.js!');
+ console.log('Updating print.js...');
} console
[HMR] Waiting for update signal from WDS... main.js:4395 [WDS] Hot Module Replacement enabled. + 2main.js:4395 [WDS] App updated. Recompiling... + main.js:4395 [WDS] App hot update... + main.js:4330 [HMR] Checking for updates on the server... + main.js:10024 Accepting the updated printMe module! + 0.4b8ee77….hot-update.js:10 Updating print.js... + main.js:4330 [HMR] Updated modules: + main.js:4330 [HMR] - 20
Через API Node.js
При использовании Webpack Dev Server с API Node.js не помещайте параметры сервера разработки в объект конфигурации webpack. Вместо этого передавайте их в качестве второго параметра при создании. Например:
new WebpackDevServer(compiler, options)
Для включения HMR вам также необходимо изменить объект конфигурации webpack, чтобы включить точки входа HMR. Пакет webpack-dev-server содержит метод addDevServerEntrypoints, который можно использовать для этого. Вот небольшой пример того, как это может выглядеть:
dev-server.js
const webpackDevServer = require('webpack-dev-server');
const webpack = require('webpack');
const config = require('./webpack.config.js');
const options = {
contentBase: './dist',
hot: true,
host: 'localhost',
};
webpackDevServer.addDevServerEntrypoints(config, options);
const compiler = webpack(config);
const server = new webpackDevServer(compiler, options);
server.listen(5000, 'localhost', () => {
console.log('dev server listening on port 5000');
}); Если вы используете
webpack-dev-middleware, ознакомьтесь с пакетомwebpack-hot-middlewareдля включения HMR на вашем собственном сервере разработки.
Особенности
Замена модулей в режиме реального времени может быть сложной. Для демонстрации вернемся к нашему рабочему примеру. Если вы нажмете кнопку на странице примера, вы обнаружите, что консоль печатает старую функцию printMe.
Это происходит потому, что обработчик события кнопки onclick по-прежнему привязан к исходной функции printMe.
Чтобы это работало с HMR, нам нужно обновить эту привязку к новой функции printMe с помощью module.hot.accept:
index.js
import _ from 'lodash';
import printMe from './print.js';
function component() {
const element = document.createElement('div');
const btn = document.createElement('button');
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
btn.innerHTML = 'Click me and check the console!';
btn.onclick = printMe; // onclick event is bind to the original printMe function
element.appendChild(btn);
return element;
}
- document.body.appendChild(component());
+ let element = component(); // Store the element to re-render on print.js changes
+ document.body.appendChild(element);
if (module.hot) {
module.hot.accept('./print.js', function() {
console.log('Accepting the updated printMe module!');
- printMe();
+ document.body.removeChild(element);
+ element = component(); // Re-render the "component" to update the click handler
+ document.body.appendChild(element);
})
} Это всего лишь один пример, но есть и другие, которые легко сбить с толку. К счастью, существует множество загрузчиков (некоторые из них перечислены ниже), которые значительно упростят работу с заменой модулей в режиме реального времени.
HMR со стилями
Замена модулей в режиме реального времени со стилями CSS на самом деле довольно проста с помощью style-loader. Этот загрузчик использует module.hot.accept в фоновом режиме для изменения тегов <style> при обновлении зависимостей CSS.
Сначала установим оба загрузчика с помощью следующей команды:
npm install --save-dev style-loader css-loader
Теперь обновим файл конфигурации, чтобы использовать загрузчик.
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
hot: true,
},
+ module: {
+ rules: [
+ {
+ test: /\.css$/,
+ use: ['style-loader', 'css-loader'],
+ },
+ ],
+ },
plugins: [
// new CleanWebpackPlugin(['dist/*']) for < v2 versions of CleanWebpackPlugin
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Hot Module Replacement',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
}; Загрузка стилей в режиме реального времени так же проста, как их импорт в модуль:
project
webpack-demo
| - package.json
| - webpack.config.js
| - /dist
| - bundle.js
| - /src
| - index.js
| - print.js
+ | - styles.css styles.css
body {
background: blue;
} index.js
import _ from 'lodash';
import printMe from './print.js';
+ import './styles.css';
function component() {
const element = document.createElement('div');
const btn = document.createElement('button');
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
btn.innerHTML = 'Click me and check the console!';
btn.onclick = printMe; // onclick event is bind to the original printMe function
element.appendChild(btn);
return element;
}
let element = component();
document.body.appendChild(element);
if (module.hot) {
module.hot.accept('./print.js', function() {
console.log('Accepting the updated printMe module!');
document.body.removeChild(element);
element = component(); // Re-render the "component" to update the click handler
document.body.appendChild(element);
})
} Измените стиль на body на background: red;, и вы сразу увидите, как цвет фона страницы изменится без полной перезагрузки.
styles.css
body {
- background: blue;
+ background: red;
} Другой код и фреймворки
В сообществе существует множество других загрузчиков и примеров, которые позволяют HMR взаимодействовать с различными фреймворками и библиотеками…
- React Hot Loader: Изменяйте компоненты React в режиме реального времени.
- Vue Loader: Этот загрузчик поддерживает HMR для компонентов Vue «из коробки».
- Elm Hot webpack Loader: Поддерживает HMR для языка программирования Elm.
- Angular HMR: Нет необходимости в загрузчике! Необходимо внести лишь небольшие изменения в файл вашего основного модуля NgModule, чтобы получить полный контроль над API HMR.
- Svelte Loader: Этот загрузчик поддерживает HMR для компонентов Svelte «из коробки».
Если вы знаете о других загрузчиках или плагинах, которые помогают или улучшают HMR, отправьте запрос на добавление в этот список!
© JS Foundation and other contributors
Licensed under the Creative Commons Attribution License 4.0.
https://v4.webpack.js.org/guides/hot-module-replacement