W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
模塊熱替換(hot module replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允許在運行時更新所有類型的模塊,而無需完全刷新。本頁面重點介紹其實現(xiàn),而 概念 頁面提供了更多關(guān)于它的工作原理以及為什么它有用的細節(jié)。
此功能可以很大程度提高生產(chǎn)效率。我們要做的就是更新 webpack-dev-server 配置, 然后使用 webpack 內(nèi)置的 HMR 插件。我們還要刪除掉 print.js 的入口起點, 因為現(xiàn)在已經(jīng)在 ?index.js
? 模塊中引用了它。
從 ?webpack-dev-server v4.0.0
? 開始,熱模塊替換是默認開啟的。
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
- print: './src/print.js',
},
devtool: 'inline-source-map',
devServer: {
static: './dist',
+ hot: true,
},
plugins: [
new HtmlWebpackPlugin({
title: 'Hot Module Replacement',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
你也可以為 HMR 提供入口:
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
+ const webpack = require("webpack");
module.exports = {
entry: {
app: './src/index.js',
- print: './src/print.js',
+ // Runtime code for hot module replacement
+ hot: 'webpack/hot/dev-server.js',
+ // Dev server client for web socket transport, hot and live reload logic
+ client: 'webpack-dev-server/client/index.js?hot=true&live-reload=true',
},
devtool: 'inline-source-map',
devServer: {
static: './dist',
+ // Dev server client for web socket transport, hot and live reload logic
+ hot: false,
+ client: false,
},
plugins: [
new HtmlWebpackPlugin({
title: 'Hot Module Replacement',
}),
+ // Plugin for hot module replacement
+ new webpack.HotModuleReplacementPlugin(),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
現(xiàn)在,我們來修改 index.js 文件,以便當 print.js 內(nèi)部發(fā)生變更時可以告訴 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();
+ })
+ }
更改 print.js 中 console.log 的輸出內(nèi)容,你將會在瀏覽器中看到如下的輸出 (不要擔心現(xiàn)在 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
在 Node.js API 中使用 webpack dev server 時,不要將 dev server 選項放在 webpack 配置對象中。而是在創(chuàng)建時, 將其作為第二個參數(shù)傳遞。例如:
new WebpackDevServer(options, compiler)
想要啟用 HMR,還需要修改 webpack 配置對象,使其包含 HMR 入口起點。這是關(guān)于如何使用的一個基本示例:
dev-server.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const webpackDevServer = require('webpack-dev-server');
const config = {
mode: 'development',
entry: [
// Runtime code for hot module replacement
'webpack/hot/dev-server.js',
// Dev server client for web socket transport, hot and live reload logic
'webpack-dev-server/client/index.js?hot=true&live-reload=true',
// Your entry
'./src/index.js',
],
devtool: 'inline-source-map',
plugins: [
// Plugin for hot module replacement
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
title: 'Hot Module Replacement',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
const compiler = webpack(config);
// `hot` and `client` options are disabled because we added them manually
const server = new webpackDevServer({ hot: false, client: false }, compiler);
(async () => {
await server.start();
console.log('dev server is running');
})();
查看 [webpack-dev-server 的 Node.js API 的完整文檔]。
模塊熱替換可能比較難以掌握。為了說明這一點,我們回到剛才的示例中。如果你繼續(xù)點擊示例頁面上的按鈕, 你會發(fā)現(xiàn)控制臺仍在打印舊的 printMe 函數(shù)。
這是因為按鈕的 onclick 事件處理函數(shù)仍然綁定在舊的 printMe 函數(shù)上。
為了讓 HMR 正常工作,我們需要更新代碼,使用 module.hot.accept 將其綁定到新的 printMe 函數(shù)上:
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(); // 存儲 element,以在 print.js 修改時重新渲染
+ 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(); // 重新渲染 "component",以便更新 click 事件處理函數(shù)
+ document.body.appendChild(element);
})
}
這僅僅是一個示例,還有很多讓人易于犯錯的情況。 幸運的是,有很多 loader(下面會提到一些)可以使得模塊熱替換變得更加容易。
借助于 style-loader,使用模塊熱替換來加載 CSS 實際上極其簡單。此 loader 在幕后使用了 module.hot.accept,在 CSS 依賴模塊更新之后,會將其 patch(修補) 到 <style> 標簽中。
首先使用以下命令安裝兩個 loader :
npm install --save-dev style-loader css-loader
然后更新配置文件,使用這兩個 loader。
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
},
devtool: 'inline-source-map',
devServer: {
static: './dist',
hot: true,
},
+ module: {
+ rules: [
+ {
+ test: /\.css$/,
+ use: ['style-loader', 'css-loader'],
+ },
+ ],
+ },
plugins: [
new HtmlWebpackPlugin({
title: 'Hot Module Replacement',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
熱加載樣式表也可以通過將它們引入一個模塊來實現(xiàn):
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 的 style 改為 background: red;,你應該可以立即看到頁面的背景顏色隨之更改,而無需完全刷新。
styles.css
body {
- background: blue;
+ background: red;
}
社區(qū)還提供許多其他 loader 和示例,可以使 HMR 與各種框架和庫平滑地進行交互……
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: