/** * 视图引擎中间件 - 纯粹的模板渲染职责 * * 职责: * 1. 提供模板渲染能力 * 2. 处理基础的渲染上下文 * 3. 错误处理和日志记录 * * 不负责: * - 业务数据获取 * - 复杂的上下文构建 * - 服务层调用 */ import consolidate from 'consolidate' import { resolve } from 'path' import { logger } from '@/logger' export default function viewsMiddleware(viewPath, options = {}) { const { extension = 'pug', options: renderOptions = {} } = options return async function views(ctx, next) { if (ctx.render) return await next() /** * 渲染模板 * @param {string} templatePath - 模板路径 * @param {object} locals - 本地变量 * @param {object} options - 渲染选项 */ ctx.response.render = ctx.render = async function(templatePath, locals = {}, options = {}) { const fullPath = resolve(viewPath, `${templatePath}.${extension}`) // 基础上下文数据(只包含框架级别的数据) const baseContext = { currentPath: ctx.path, isLogin: !!(ctx.state && ctx.state.user), } // 合并上下文:基础上下文 + locals + ctx.state + 渲染选项 const renderContext = Object.assign( {}, baseContext, locals, renderOptions, ctx.state || {}, options ) // 添加 partials 支持 renderContext.partials = Object.assign({}, renderOptions.partials || {}) ctx.type = 'text/html' const render = consolidate[extension] if (!render) { throw new Error(`Template engine not found for ".${extension}" files`) } try { const html = await render(fullPath, renderContext) ctx.body = html return html } catch (err) { logger.error('View rendering error:', { template: templatePath, fullPath, error: err.message, stack: err.stack }) throw err } } return await next() } }