中间件插件开发
中间件插件开发的基本概念
Vite.js 中间件插件本质上是拦截和转换请求/响应的函数。它们工作在开发服务器和浏览器之间,能够修改请求、响应或执行自定义逻辑。中间件插件的核心是一个接收 server
对象并返回中间件函数的工厂函数。
interface VitePluginMiddleware {
(server: ViteDevServer): Connect.NextHandleFunction;
}
典型的中间件函数签名遵循 Connect/Express 风格,包含 req
、res
和 next
参数。这种设计使得来自 Node.js 生态的开发者能够快速上手。
创建基础中间件插件
开发一个简单的日志中间件插件:
// vite-plugin-logger.ts
import type { Plugin, ViteDevServer } from 'vite';
export default function loggerPlugin(): Plugin {
return {
name: 'vite-plugin-logger',
configureServer(server) {
server.middlewares.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
});
}
};
}
在 vite.config.ts 中使用这个插件:
import { defineConfig } from 'vite';
import loggerPlugin from './vite-plugin-logger';
export default defineConfig({
plugins: [loggerPlugin()]
});
这个基础示例会在控制台输出每个请求的方法和URL,展示了中间件插件最基本的拦截能力。
请求/响应处理技术
修改请求头
中间件可以修改即将发送到Vite服务器的请求:
server.middlewares.use((req, res, next) => {
req.headers['x-custom-header'] = 'vite-middleware';
next();
});
拦截特定请求
实现一个API请求代理的常见模式:
server.middlewares.use('/api', (req, res, next) => {
if (req.url?.startsWith('/api/users')) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify([{ id: 1, name: 'Test User' }]));
return;
}
next();
});
响应转换
修改Vite服务器返回的响应内容:
const transformMiddleware = (req, res, next) => {
const originalWrite = res.write;
res.write = function (chunk) {
if (typeof chunk === 'string' && res.getHeader('content-type')?.includes('text/html')) {
chunk = chunk.replace('</head>', '<script>console.log("Injected!")</script></head>');
}
return originalWrite.call(this, chunk);
};
next();
};
高级中间件模式
中间件组合
将多个中间件组合成一个处理链:
function composeMiddlewares(middlewares: Connect.NextHandleFunction[]) {
return (req, res, next) => {
let index = -1;
function dispatch(i: number) {
if (i <= index) return next(new Error('next() called multiple times'));
index = i;
const middleware = middlewares[i] || next;
middleware(req, res, (err?: any) => {
if (err) return next(err);
dispatch(i + 1);
});
}
dispatch(0);
};
}
// 使用示例
server.middlewares.use(composeMiddlewares([
authMiddleware,
loggingMiddleware,
transformMiddleware
]));
热更新集成
中间件可以与Vite的热更新机制(HMR)结合:
server.middlewares.use((req, res, next) => {
if (req.url === '/__custom_hmr') {
server.ws.send({ type: 'custom-update', data: Date.now() });
res.end('HMR triggered');
return;
}
next();
});
实际应用案例
Mock API 服务器
开发环境下替代真实后端API的完整示例:
// vite-plugin-mock-api.ts
import type { Plugin, ViteDevServer } from 'vite';
import { createMockServer } from 'vite-plugin-mock';
interface MockApiOptions {
mockDir?: string;
enable?: boolean;
}
export default function mockApiPlugin(options: MockApiOptions = {}): Plugin {
return {
name: 'vite-plugin-mock-api',
configureServer(server) {
if (options.enable === false) return;
const mockServer = createMockServer({
mockPath: options.mockDir || 'mock',
watchFiles: true
});
server.middlewares.use('/api', (req, res, next) => {
if (mockServer.has(req.url!)) {
mockServer.handle(req, res);
} else {
next();
}
});
}
};
}
权限控制中间件
实现基于角色的访问控制:
// vite-plugin-auth.ts
import type { Plugin, ViteDevServer } from 'vite';
interface AuthOptions {
roles: string[];
}
export default function authPlugin(options: AuthOptions): Plugin {
return {
name: 'vite-plugin-auth',
configureServer(server) {
server.middlewares.use((req, res, next) => {
const token = req.headers['authorization'];
const userRole = getUserRoleFromToken(token); // 假设的实现
if (!options.roles.includes(userRole)) {
res.writeHead(403);
res.end('Forbidden');
return;
}
next();
});
}
};
}
function getUserRoleFromToken(token?: string | string[]): string {
// 实际项目中应该实现JWT解析等逻辑
return 'admin';
}
性能优化技巧
缓存策略
实现静态资源缓存:
server.middlewares.use('/assets', (req, res, next) => {
if (req.method === 'GET') {
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
}
next();
});
延迟加载中间件
按需加载资源密集型中间件:
function lazyMiddleware(factory: () => Connect.NextHandleFunction) {
let middleware: Connect.NextHandleFunction | null = null;
return (req, res, next) => {
if (!middleware) {
middleware = factory();
}
middleware(req, res, next);
};
}
// 使用示例
server.middlewares.use(lazyMiddleware(() => {
const heavyMiddleware = require('heavy-middleware');
return heavyMiddleware({ /* 配置 */ });
}));
调试与错误处理
错误捕获中间件
统一处理中间件链中的错误:
server.middlewares.use((err: Error, req, res, next) => {
console.error('Middleware error:', err);
res.writeHead(500);
res.end('Internal Server Error');
});
// 触发错误的示例中间件
server.middlewares.use('/error', (req, res, next) => {
next(new Error('Test error'));
});
开发时调试工具
集成调试日志:
function createDebugMiddleware(namespace: string) {
return (req, res, next) => {
const start = Date.now();
const originalEnd = res.end;
res.end = function (...args: any[]) {
const duration = Date.now() - start;
console.log(`[${namespace}] ${req.method} ${req.url} - ${duration}ms`);
originalEnd.apply(res, args);
};
next();
};
}
与Vite生态系统的集成
与现有插件协作
处理其他插件添加的中间件:
server.middlewares.stack.unshift({
route: '',
handle: yourMiddleware,
id: 'your-plugin-id'
});
访问Vite内部模块
利用Vite的内部API实现高级功能:
import { transformWithEsbuild } from 'vite';
server.middlewares.use('/transform', async (req, res) => {
const code = await getRequestBody(req);
const result = await transformWithEsbuild(code, 'file.js', {
loader: 'jsx',
minify: true
});
res.writeHead(200, { 'Content-Type': 'application/javascript' });
res.end(result.code);
});
生产环境注意事项
条件注册中间件
只在开发环境启用某些中间件:
export default function devOnlyPlugin(): Plugin {
return {
name: 'vite-plugin-dev-only',
apply: 'serve', // 关键配置
configureServer(server) {
server.middlewares.use(devMiddleware);
}
};
}
安全中间件
添加基本安全防护:
import helmet from 'helmet';
server.middlewares.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", 'https:'],
styleSrc: ["'self'", "'unsafe-inline'"]
}
}
}));
测试中间件插件
单元测试示例
使用supertest测试中间件:
import request from 'supertest';
import { createServer } from 'vite';
describe('Middleware Plugin', () => {
let server: ViteDevServer;
beforeAll(async () => {
server = await createServer({
configFile: false,
plugins: [yourMiddlewarePlugin()]
});
await server.listen(3000);
});
afterAll(async () => {
await server.close();
});
it('should handle /api route', async () => {
const response = await request(server.httpServer)
.get('/api/test')
.expect(200);
expect(response.body).toEqual({ success: true });
});
});
集成测试策略
模拟完整请求生命周期:
function testMiddleware(middleware: Connect.NextHandleFunction) {
return (req: any = {}, res: any = {}) => {
req.url = req.url || '/';
req.method = req.method || 'GET';
req.headers = req.headers || {};
return new Promise((resolve, reject) => {
res.end = (data: any) => {
res.body = data;
resolve(res);
};
res.writeHead = (status: number, headers: any) => {
res.statusCode = status;
res.headers = { ...res.headers, ...headers };
};
middleware(req, res, (err?: any) => {
if (err) return reject(err);
resolve(res);
});
});
};
}
进阶主题:中间件元编程
动态中间件注册
运行时根据条件注册中间件:
function dynamicMiddlewareRouter(routes: Record<string, Connect.NextHandleFunction>) {
return (req, res, next) => {
const match = Object.entries(routes).find(([path]) =>
req.url?.startsWith(path)
);
if (match) {
return match[1](req, res, next);
}
next();
};
}
// 使用示例
server.middlewares.use(dynamicMiddlewareRouter({
'/api': apiMiddleware,
'/graphql': graphqlMiddleware,
'/ws': websocketMiddleware
}));
中间件热替换
开发时更新中间件逻辑而不重启服务器:
function hotMiddlewareWrapper(factory: () => Connect.NextHandleFunction) {
let currentMiddleware = factory();
if (import.meta.hot) {
import.meta.hot.accept('./middleware-implementation', () => {
currentMiddleware = factory();
});
}
return (req, res, next) => currentMiddleware(req, res, next);
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:Rollup插件兼容性处理