webpack-dev-server源码分析1:启动http服务/express,http升级WebSocket服务,webpack的watch

656 阅读2分钟

启动一个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()
    }