跳到主要内容

柏拉文

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 中的内容, 观看效果