阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 中间件插件开发

中间件插件开发

作者:陈川 阅读数:10825人阅读 分类: 构建工具

中间件插件开发的基本概念

Vite.js 中间件插件本质上是拦截和转换请求/响应的函数。它们工作在开发服务器和浏览器之间,能够修改请求、响应或执行自定义逻辑。中间件插件的核心是一个接收 server 对象并返回中间件函数的工厂函数。

interface VitePluginMiddleware {
  (server: ViteDevServer): Connect.NextHandleFunction;
}

典型的中间件函数签名遵循 Connect/Express 风格,包含 reqresnext 参数。这种设计使得来自 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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌