性能优化
用 MJML 构建跨客户端响应式邮件模板(Outlook/G…
需要发送事务性邮件(注册、重置密码、发票、通知)并保证在各邮件客户端正确渲染时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
用 MJML 构建跨客户端响应式邮件模板通用
解决思路
- 安装 Mjml.Net:运行 `dotnet add package Mjml.Net`。
- 在 `.csproj` 中把模板作为嵌入资源:添加 `<ItemGroup><EmbeddedResource Include="Templates\**\*.mjml" /></ItemGroup>`。
- 按约定组织项目结构:`Templates/` 下放 `_Layout.mjml` 共享布局,并按业务分目录(如 `UserInvitations/`、`PasswordReset/`、`Billing/`);`Mjml/` 放渲染器接口与实现;`Composers/` 放邮件组装器。
- 编写 `_Layout.mjml` 共享布局:在 `<mj-head>` 中设置 `mj-title`、`mj-preview`、`mj-attributes`(统一字体、字号、颜色、行高、padding)和 `mj-style inline="inline"`;在 `<mj-body>` 中放 header(logo 图片)、内容注入区(`{{Content}}`)和 footer。
- 编写内容模板:只写内容片段(如 `mj-text`、`mj-button`),不包含 `<mjml>` 外壳,因为它会被自动包裹进 `_Layout.mjml`。
- 实现 `IMjmlTemplateRenderer` 接口,提供 `RenderTemplateAsync(templateName, variables, ct)` 方法。
- 在渲染器中加载内容模板,再加载 `_Layout`,用 `layoutMjml.Replace("{{Content}}", contentMjml)` 注入内容。
- 合并变量:先放入 `SiteUrl` 等全局变量,再合并调用方传入的模板变量。
- 用正则 `\{\{([^}]+)\}\}` 替换变量,未匹配到的保留原样。
- 调用 `MjmlRenderer.RenderAsync` 编译为 HTML;若 `result.Errors` 非空则抛出 `InvalidOperationException` 并拼接错误信息。
- 从程序集嵌入资源读取模板:资源名格式为 `命名空间.Templates.模板名(斜杠替换为点).mjml`,找不到时抛 `FileNotFoundException`。
- 使用 Composer 模式分离渲染与邮件组装:定义 `IUserEmailComposer` 接口,方法参数使用强类型值对象(如 `EmailAddress`、`PersonName`、`OrganizationName`、`AbsoluteUri`、`DateTimeOffset`)。
- 在 Composer 中构造变量字典(如 `PreviewText`、`InviteeName`、`InviterName`、`OrganizationName`、`InvitationLink`、`ExpirationDate`),调用渲染器并返回 `EmailMessage`(含 To、Subject、HtmlBody)。
- 添加管理员预览端点:`app.MapGet("/admin/emails/preview/{template}", ...)`,用示例变量渲染并返回 `Results.Content(html, "text/html")`,并用 `.RequireAuthorization("AdminOnly")` 保护。
- 模板设计遵循最佳实践:用 MJML 组件(`mj-section`/`mj-column`/`mj-text`)布局,不要用原生 HTML `<table>`;图片使用生产环境绝对 URL(如 `https://myapp.com/logo.png`),不要用相对路径。
- 变量处理遵循最佳实践:Composer 方法使用强类型值对象参数,不要用裸 `string`。
- 使用 MJML 组件:`mj-section`(行容器)、`mj-column`(列容器)、`mj-text`(文本)、`mj-button`(按钮)、`mj-image`(响应式图片)、`mj-divider`(分割线)、`mj-spacer`(垂直间距)、`mj-table`(数据表格)、`mj-social`(社交图标)。
适用边界 · 注意事项
- 不使用 .NET 技术栈时(本技能示例基于 Mjml.Net 与 C#)。
- 不需要跨客户端兼容、仅内部纯文本通知的场景,无需引入 MJML 编译流程。
- 无法将模板作为嵌入资源打包或无法访问程序集资源的运行环境。