柏拉文
2024年04月07日
一、服务端
1.1 test/webpack/bolawen01/hmr/server.js
const Path = require('path');
const mime = require('mime');
const http = require('http');
const express = require('express');
const webpack = require('webpack');
const socketIo = require('socket.io');
const MemoryFileSystem = require('memory-fs');
const webpackConfig = require('./webpack.config.js');
class Server {
constructor(compiler) {
this.compiler = compiler;
let sockets = [];
let lastHash;
compiler.hooks.done.tap('webpack-dev-server', stats => {
lastHash = stats.hash;
sockets.forEach(socket => {
console.log('stats.hash', stats.hash);
socket.emit('hash', stats.hash);
socket.emit('ok');
});
});
let app = new express();
compiler.watch({}, err => {
console.log('编译完成');
});
let fs = new MemoryFileSystem();
compiler.outputFileSystem = fs;
function middleware(req, res, next) {
if (req.url === '/favicon.ico') {
return res.sendStatus(404);
}
try {
let filename = Path.join(webpackConfig.output.path, req.url.slice(1));
let stat = fs.statSync(filename);
if (stat.isFile()) {
let content = fs.readFileSync(filename);
let contentType = mime.getType(filename);
res.setHeader('Content-Type', contentType);
res.statusCode = res.statusCode || 200;
res.send(content);
} else {
return res.sendStatus(404);
}
} catch (error) {
return res.sendStatus(404);
}
}
app.use(middleware);
this.server = http.createServer(app);
let io = socketIo(this.server);
io.on('connection', socket => {
sockets.push(socket);
socket.emit('hash', '');
socket.emit('ok');
});
}
listen(port) {
this.server.listen(port, () => {
console.log(`server start ${port}`);
});
}
}
const compiler = webpack(webpackConfig);
let server = new Server(compiler);
server.listen(4000);
1.2 test/webpack/bolawen01/hmr/webpack.config.js
const Path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: Path.resolve(__dirname, './index.js'),
output: {
filename: 'main.js',
path: Path.resolve(__dirname, 'dist')
},
devServer: {
hot: true,
static: {
directory: Path.resolve(__dirname, 'dist')
}
},
plugins: [
new HtmlWebpackPlugin({
template: Path.resolve(__dirname, './index.html')
}),
new webpack.HotModuleReplacementPlugin()
]
};
二、客户端
2.1 test/webpack/bolawen01/hmr/client.js
let lastHash;
let socket = io('/');
const status = {
currentHash: typeof __webpack_hash__ !== 'undefined' ? __webpack_hash__ : ''
};
const options = {
hot: true
};
class Emitter {
constructor() {
this.listeners = {};
}
on(type, listener) {
this.listeners[type] = listener;
}
emit(type, ...args) {
this.listeners[type] && this.listeners[type].apply(null, args);
}
}
function check() {
module.hot.check(true);
}
const hotEmitter = new Emitter();
hotEmitter.on('webpackHotUpdate', function (currentHash) {
lastHash = currentHash;
if (module.hot.status() === 'idle') {
check();
}
});
function reloadApp(options, status) {
const { hot } = options;
const { currentHash, previousHash } = status;
const isInitial = currentHash.indexOf(previousHash) >= 0;
if (isInitial) {
return;
}
if (hot) {
hotEmitter.emit('webpackHotUpdate', status.currentHash);
} else {
window.location.reload();
}
}
socket.on('hash', hash => {
status.previousHash = status.currentHash;
status.currentHash = hash || '';
});
socket.on('ok', () => {
reloadApp(options, status);
});
2.2 test/webpack/bolawen01/hmr/index.js
import './client.js';
const root = document.getElementById('root');
const input = document.createElement('input');
input.placeholder = 'HMR Input';
document.body.appendChild(input);
function render() {
root.innerHTML = require('./content.js');
}
render();
if (module.hot) {
module.hot.accept(['./content.js'], () => {
render();
});
}
2.3 test/webpack/bolawen01/hmr/content.js
const content = 'Hello Webpack Hot Module Replacement 005';
module.exports = content;
2.4 test/webpack/bolawen01/hmr/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Webpack Hot Module Replacement</title>
</head>
<body>
<div id="root"></div>
<script src="/socket.io/socket.io.js"></script>
</body>
</html>
三、测试
3.1 命令
"hmr-dev-server": "node ./hmr/server.js"
3.2 访问
浏览器访问 http://localhost:4000/index.html
即可
3.3 更新
修改 content.js
中的内容, 观看效果