You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在EJS模板中导入CSS样式并邮件发送,同时维持现有文件结构

解决Express+EJS+Nodemailer邮件样式内联与模板分离问题

邮件客户端几乎都不支持外部CSS文件引用,所以核心思路是把所有分离的CSS合并后内联到HTML中,同时完全保留你现有的文件结构,解决授权和样式失效的问题。


1. 编写CSS合并工具函数

你的样式分为三级(全局→组→模板自身),按优先级读取合并(自身样式覆盖组和全局样式),用Node.js的fs和path模块实现:

import fs from 'fs/promises';
import path from 'path';

// 合并指定模板的各级CSS
async function getCombinedStyles(viewGroup: 'internal_view_group' | 'public_view_group', templateName: string): Promise<string> {
  const styles: string[] = [];
  const basePath = './views';

  // 读取全局样式
  try {
    const globalStyle = await fs.readFile(path.join(basePath, 'style.css'), 'utf8');
    styles.push(globalStyle);
  } catch (err) {
    console.warn('全局style.css未找到,跳过');
  }

  // 读取组级样式
  try {
    const groupStyle = await fs.readFile(path.join(basePath, viewGroup, 'style.css'), 'utf8');
    styles.push(groupStyle);
  } catch (err) {
    console.warn(`${viewGroup}组style.css未找到,跳过`);
  }

  // 读取模板自身样式(优先级最高)
  try {
    const templateStyle = await fs.readFile(path.join(basePath, viewGroup, templateName, 'style.css'), 'utf8');
    styles.push(templateStyle);
  } catch (err) {
    console.warn(`${templateName}模板style.css未找到,跳过`);
  }

  // 合并所有样式,后添加的样式会覆盖前面的规则
  return styles.join('\n');
}

2. 渲染EJS时注入内联样式

在Express路由中先合并对应模板的CSS,再将样式作为变量传给EJS,在模板内用<style>标签包裹实现内联:

Express路由示例

import express from 'express';
import { renderFile } from 'ejs';
import nodemailer from 'nodemailer';
import { getCombinedStyles } from './utils/css-combiner';
import path from 'path';

const router = express.Router();

// 发送邮件接口
router.post('/send-email', async (req, res) => {
  const { viewGroup, templateName, lang, recipientEmail } = req.body;
  
  // 1. 合并当前模板的所有层级样式
  const combinedStyles = await getCombinedStyles(viewGroup, templateName);

  // 2. 渲染指定语言的EJS模板,传入合并后的样式变量
  const emailHtml = await renderFile(
    path.join('./views', viewGroup, templateName, `${lang}.ejs`),
    { combinedStyles }
  );

  // 3. 配置Nodemailer并发送邮件
  const transporter = nodemailer.createTransport({
    host: 'smtp.your-domain.com',
    port: 587,
    secure: false,
    auth: {
      user: 'your-account@your-domain.com',
      pass: 'your-auth-pass'
    }
  });

  await transporter.sendMail({
    from: 'your-account@your-domain.com',
    to: recipientEmail,
    subject: '带样式的测试邮件',
    html: emailHtml // 直接发送内联了样式的HTML内容
  });

  res.status(200).send('邮件发送成功');
});

EJS模板修改(以en-us.ejs为例)

在模板的<head>中注入合并后的样式:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>邮件标题</title>
  <!-- 内联合并后的所有样式 -->
  <style><%= combinedStyles %></style>
</head>
<body>
  <!-- 原有模板内容 -->
  <div class="template-container">
    <h1>Hello, <%= username %></h1>
    <p>This is a styled email.</p>
  </div>
</body>
</html>

方案优势

  • 完全保留现有文件结构:全局、组、模板三级样式仍分离存放,维护逻辑不变
  • 解决授权问题:无需外部样式路由,所有样式内嵌在HTML中,邮件客户端直接读取
  • 解决样式失效问题:内联样式是邮件客户端兼容的标准写法,适配绝大多数邮箱服务商

内容的提问来源于stack exchange,提问作者Nekit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 19:15:35