前言
webpack-dev-server 底层是 express
+ webpack-dev-middleware
。
express
是基础。
webpack-dev-middleware
是中间件,以监听模式启动 webpack,将编译后的文件输出到内存(使用fs-memory
),沟通webpack
的HRM
(热替换 Hot Module Replacement)API便于本地开发的实时更新。
以伪代码来看,大致上是这么一个过程。
const express = require('express');
const webpackDevMiddleware= require('webpack-dev-middleware');
const app = express();
const devServer = app.use( webpackDevMiddleware(config) )
看到这个过程,我们很自然的想到,我们是不是可以给express
加上更多中间件,来升级整个Webpack DevServer
的能力呢?
Webpack devServer
提供了这个注册第三方中间件的入口。
注册中间件
官方文档:
webpack.docschina.org/configuration/dev-server/#devserversetupmiddlewares
www.expressjs.com.cn/guide/using-middleware.html
截止今天(2022.10.31),最新版本webpack-dev-server@4.11.1
,
官方还兼容2个老的接口。
devServer.onAfterSetupMiddleware
devServer.onBeforeSetupMiddleware
不过已经depreciated
了。
推荐以后采用devServer.setupMiddlewares
。
看官方示例:
module.exports = {
// ...
devServer: {
setupMiddlewares: (middlewares, devServer) => {
if (!devServer) {
throw new Error('webpack-dev-server is not defined');
}
devServer.app.get('/setup-middleware/some/path', (_, response) => {
response.send('setup-middlewares option GET');
});
// 如果你想在所有其他中间件之前运行一个中间件或者当你从 `onBeforeSetupMiddleware` 配置项迁移时,
// 可以使用 `unshift` 方法
middlewares.unshift({
name: 'first-in-array',
// `path` 是可选的
path: '/foo/path',
middleware: (req, res) => {
res.send('Foo!');
},
});
// 如果你想在所有其他中间件之后运行一个中间件或者当你从 `onAfterSetupMiddleware` 配置项迁移时,
// 可以使用 `push` 方法
middlewares.push({
name: 'hello-world-test-one',
// `path` 是可选的
path: '/foo/bar',
middleware: (req, res) => {
res.send('Foo Bar!');
},
});
middlewares.push((req, res) => {
res.send('Hello World!');
});
return middlewares;
},
},
};
上面这个示例,我们在配置文件中,为setupMiddlewares字段设置了一个function类型的值。
实际调用的是node_modules/webpack-dev-server/lib/Server.js
中 Server类下的成员函数 setupMiddlewares()
。
这个函数中,如果检测到传入的配置字段是function类型,就会把内部的middlewares数组和自身的server实例传进去。
返回值是新的middlewares数组。
所以我们在配置文件config.js中用到的签名是 setupMiddlewares: (middlewares, devServer) => {}
。
下一段就会middlewares.foreach
,使用(this.app).use
进行真正的注册。
setupMiddlewares
支持几种方式注册中间件。
2.1 支持直接操作middlewares数组。
数组元素支持
(req,res)=>{}
Function格式{path:'', name:'', middleware:(req,res)=>{} }
Object格式
const mymiddleware = require('path/to/mymiddleware');
setupMiddlewares: (middlewares, devServer) => {
//函数形式
middlewares.push((req,res)=>{}); // push 最后注册
middlewares.unshift((req,res)=>{}); // unshift最先注册
return middlewares; // setupMiddlewares必须返回middlewares数组
}
setupMiddlewares: (middlewares, devServer) => {
//对象形式
middlewares.push({path:'/api',middleware:mymiddleware }); // push 最后注册
middlewares.unshift({path:'/api',middleware:mymiddleware }); // unshift最先注册
return middlewares; // setupMiddlewares必须返回middlewares数组
}
2.2 直接操作app注册
const mymiddleware = (req,res)=>{};
setupMiddlewares: (middlewares, devServer) => {
// 操作express app注册
const app = devServer.app;
app.use(mymiddleware);
// 或者直接写
devServer.app.use(mymiddleware);
// 操作app的形式也支持注册到path
devServer.app.use('/api',mymiddleware);
devServer.app.get('/api',mymiddleware);
return middlewares; // setupMiddlewares必须返回middlewares数组
}
需要注意的是,app.use( ‘somepath’, middleware )的方式,
和直接app.use(middleware ) 的区别在于,
首先,前者只会在路径匹配到 ‘somepath’ 的时候才执行后面的middleware。
而后者的path默认是 ‘/’ ,所以会对所有请求都依次应用middlewares数组。
其次,前者注册的path会成为baseUrl, 可以在middleware中 通过req.baseUrl获得。
举例, 访问 '/api/aa/bb’时。
const mymiddleware = (req,res)=>{
console.log('baseUrl:', req.baseUrl);
console.log('path:', req.path);
};
app.use('/api',mymiddleware); // 会打印 baseUrl: /api, path: /aa/bb
app.use(mymiddleware); // 会打印 baseUrl: /, path: /api/aa/bb
Express middleware写法
上一节我们知道了,写出来一个middleware之后怎么注册。
这一节讲一下,怎么写一个express middleware。
https://www.expressjs.com.cn/4x/api.html#req