启动一个express
启动express方式一:
// 分析源码后自己实现的简洁版
const express = require('express')
const http = require('http')
this.app = new express()
this.server = http.createServer({}, this.app)
this.server.listen(9000)
启动express方式二:
const express = require('express')
this.app = express()
this.app.listen(9000)
我们来看看express的listen()方法的实现源码
app.listen = function listen(){
var server = http.createServer(this)
return server.listen.apply(server, arguments)
}
从express的listen()源码看,启动express的方式一和方式二是一样的意思; webpack-dev-server为什么不直接使用express封装好的listen()呢?
这是因为后续需要对http做升级服务,后面的内容会介绍到将http服务升级到WebSocket服务。
开启Webpack监听模式
// webpack/lib/Compiler.js
watch(watchOptions, handler){
this.running = true
this.watchMode = true
this.watching = new Watching(this, watchOptions, handler)
return this.watching
}
// webpack/lib/Watching.js
class Watching {
constructor(compiler, watchOptions, handler) {
this._initial = true
process.nextTick(() => {
if(this._initial) this._invalidate();
})
}
}
// webpack/lib/Compiler.js
readRecords(){
}
使用http.createServer()创建一个http服务,并升级WebSocket服务
协议升级机制
HTTP协议 提供了一种特殊的机制, 这一机制允许将一个已建立的连接升级成新的、不相容的协议。
升级机制的常用场合:
升级到WebSocket协议的连接
服务端:
const WebSocket = require ('ws')
const http = require('http')
const { client } = require('webpack-dev-server/bin/cli-flags')
// 新建一个http服务
const server = http.createServer()
const wss = new WebSocket.WebSocketServer({
noServer: true
})
wss.on('connection', function connection(ws) {
console.log("触发 wss 的 connection")
ws.on('message', function message(data){
console.log("服务端收到客户端的消息", JSON.parse(data))
})
ws.send("222")
})
// 每次服务器响应升级请求时触发
server.on('upgrade', function upgrade(request, socket, head){
console.log("触发server的upgrade")
wss.handleUpgrade(request, socket, head, function done(ws){
wss.emit('connection', ws, request, client)
})
})
server.listen(9000)
客户端: WebSocket()构造函数已经自动完成了发送初始HTTP1.1请求,处理握手和升级过程
const WebSocket = require ('ws')
const ws = new WebSocket('ws://127.0.0.1:9000')
ws.on('open', function open(){
ws.send(11)
})
ws.on('message', function message(data) {
console.log("客户端收到服务端的消息", JSON.parse(data));
})
webpack源码实现Websocket
@webpack-cli\serve\lib\index.js
class ServeCommand {
async apply(cli) {
const loadDevServerOptions = () => {}
await cli.makeCommand({}, async ()=>{}, async ()=> {
let server ;
server = new DevServer(complier, devServerOptions)
server.start()
})
}
}
webpack-dev-server\lib\Server.js
服务端: 开启一个http服务,并升级WebSocket服务。
async initialize(){
this.setupDevMiddleware()
this.createServer()
this.webSocketProxis.forEach((webSocketProxy) => {
// 此时即服务器响应升级请求
this.server.on("upgrade", webSocketProxy.upgrade)
}, this)
if(this.options.webSocketServer){
this.createWebSocketServer()
}
}
createServer(){
this.server = require('http').createServer({})
this.server.on("connection", (socket) => {
this.sockets.push(socket)
})
}
createWebSocketServer(){
this.webSocketServer = new WebSocketServer(this)
this.webSocketServer =.implementation.on("connection", (client, request) => {
if(this.options.hot === true || this.options.hot === "only") {
this.sendMessage([client], "hot")
}
if (this.options.liveReload) {
this.sendMessage([client], "liveReload");
}
if (this.options.client && this.options.client.progress) {
this.sendMessage([client], "progress", this.options.client.progress);
}
if (this.options.client && this.options.client.reconnect) {
this.sendMessage([client], "reconnect", this.options.client.reconnect);
}
if (this.options.client && this.options.client.overlay) {
this.sendMessage([client], "overlay", this.options.client.overlay);
}
if (shouldEmit) {
this.sendMessage(clients, "still-ok");
return;
}
this.currentHash = stats.hash;
this.sendMessage(clients, "hash", stats.hash);
if (stats.errors.length > 0 || stats.warnings.length > 0) {
} else {
this.sendMessage(clients, "ok");
}
})
}
async start() {
await this.initialize();
const listenOptions = {
host : this.options.host,
port : this.options.port
}
await new Promise((resolve) => {
this.server.listen(listenOptions, ()=> {
resolve()
})
})
}
webpack-dev-server/client/index.js
这里的代码会打包到业务代码中,当用户请求页面时,会执行
客户端:
var socketURL = createSocketURL()
// socketURL = "ws://127.0.0.1:9000/ws"
client = new Client(url)
client.onOpen()
client.onClose(function (){
})
client.onMessage(function (data) {
var message = JSON.parse(data)
})
Webpack的Watching异步触发编译
_invalidate(){
this._go()
}
_go(){
const run = () => {
if(this._needRecords){
return this.compiler.readRecords(err => {
this._needRecords = false
run()
})
}
// watchRun钩子,在监听模式下,
this.compiler.hooks.watchRun.callAsync(this.compiler, err=>{
// 开始
this.compiler.compile(onCompiled)
})
}
run()
}