webpack配置
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode:'development',
devtool:false,
entry:'./src/index.js',
output:{
filename:'main.js',
path:path.resolve(__dirname,'dist')
},
devServer:{
hot:true,
contentBase:path.resolve(__dirname,'dist')
},
plugins:[
new HtmlWebpackPlugin(),
new webpack.HotModuleReplacementPlugin()
]
}
hmr热更新原理
const webpack = require('webpack')
//配置对象
const config = require('../webpack.config')
const Server = require('./lib/server/Server')
//编译器对象
const compiler = webpack(config)
const server = new Server(compiler)
server.listen(9090,'localhost',()=>{
console.log('服务已经在9090端口上使用!')
})
utils/updateCompiler
const path = require('path');
function updateCompiler(compiler){
const config = compiler.options;
config.entry = {
main:[
path.resolve(__dirname,'../../client/index.js'),
path.resolve(__dirname,'../../../webpack/hot/dev-server.js'),
config.entry
]
}
}
module.exports = updateCompiler;
dev-serve.js
/**
* 存二个hash值,一个是上一个hash,一个是当前的hash
*
*/
let lastHash;
hotEmitter.on('webpackHotUpdate',()=>{
console.log('hotCheck');
});
client
let currentHash ;
class EventEmitter{
constructor(){
this.events = {};
}
on(eventName,fn){
this.events[eventName]=fn;
}
emit(eventName,...args){
this.events[eventName](...args);
}
}
let hotEmitter = new EventEmitter();
const socket = window.io('/');
socket.on('hash',(hash)=>{
currentHash=hash;
});
socket.on('ok',()=>{
console.log('ok');
reloadApp();
});
function reloadApp(){
hotEmitter.emit('webpackHotUpdate');
}
const express = require('express');
const http = require('http');
const path = require('path');
const fs = require('fs-extra');
fs.join = path.join;
const mime = require('mime');
const socketIO = require('socket.io');
const updateCompiler = require('../utils/updateCompiler');
class Server {
constructor(compiler){
this.compiler = compiler;
updateCompiler(compiler);
this.setupApp();
this.currentHash;
this.clientSocketList = [];
this.setupHooks();
this.setupDevMiddleware();
this.routes();
this.createServer();
this.createSocketServer();
}
createSocketServer(){
const io = socketIO(this.server);
io.on('connection',(socket)=>{
console.log('一个新的客户端已经连接上了');
this.clientSocketList.push(socket);
socket.emit('hash',this.currentHash);
socket.emit('ok');
socket.on('disconnect',()=>{
let index = this.clientSocketList.indexOf(socket);
this.clientSocketList.splice(index,1);
});
});
}
routes(){
let {compiler} = this;
let config = compiler.options;
this.app.use(this.middleware(config.output.path));
}
setupDevMiddleware(){
this.middleware = this.webpackDevMiddleware();
}
webpackDevMiddleware(){
let {compiler} = this;
compiler.watch({},()=>{
console.log('监听模式编译成功');
});
this.fs = compiler.outputFileSystem = fs;
return (staticDir)=>{
return (req,res,next)=>{
let {url} = req;
if(url === '/favicon.ico'){
return res.sendStatus(404);
}
url === '/'?url = '/index.html':null;
let filePath = path.join(staticDir,url);
try{
let statObj = this.fs.statSync(filePath);
console.log('statObj',statObj);
if(statObj.isFile()){
let content = this.fs.readFileSync(filePath);
res.setHeader('Content-Type',mime.getType(filePath));
res.send(content);
}else{
return res.sendStatus(404);
}
}catch(error){
console.log(error);
return res.sendStatus(404);
}
}
}
}
setupHooks(){
let {compiler} = this;
compiler.hooks.done.tap('webpack-dev-server',(stats)=>{
console.log('hash',stats.hash);
this.currentHash = stats.hash;
this.clientSocketList.forEach(socket=>{
socket.emit('hash',this.currentHash);
socket.emit('ok');
});
});
}
setupApp(){
this.app = express();
}
createServer(){
this.server = http.createServer(this.app);
}
listen(port,host,callback){
this.server.listen(port,host,callback);
}
}
module.exports = Server;