
ejs 官方 cli 不支持递归处理目录,但通过 node.js 脚本可轻松实现全目录 ejs 模板批量渲染——本文提供健壮、可配置的递归渲染方案,支持嵌套结构、数据注入与路径映射。
ejs 官方 cli 不支持递归处理目录,但通过 node.js 脚本可轻松实现全目录 ejs 模板批量渲染——本文提供健壮、可配置的递归渲染方案,支持嵌套结构、数据注入与路径映射。
虽然 EJS 官方 CLI(如 ejs template.ejs -o output.html)仅支持单文件渲染,但实际项目中常需将含大量 .ejs 占位符的整套模板目录(如网站骨架、配置生成器或代码脚手架)一次性渲染为纯静态结构。此时,自研轻量级递归渲染脚本是最灵活、可控且无需引入第三方 fork 的解决方案。
以下是一个生产就绪的递归渲染脚本,已增强健壮性与实用性:
const fs = require('fs');
const path = require('path');
const ejs = require('ejs');
// ✅ 可配置参数
const INPUT_DIR = 'my-template';
const OUTPUT_DIR = 'output';
const DATA_FILE = 'data.json'; // 统一数据源(支持嵌套对象)
const DEFAULT_DATA = { timestamp: new Date().toISOString(), version: '1.0.0' };
// 读取全局数据(优先使用 data.json,缺失则回退默认值)
const loadData = () => {
try {
return JSON.parse(fs.readFileSync(path.join(INPUT_DIR, DATA_FILE), 'utf8'));
} catch (e) {
console.warn(`⚠️ data.json not found in ${INPUT_DIR}, using default data.`);
return DEFAULT_DATA;
}
};
const renderFile = (inputPath, outputPath, data) => {
try {
const content = fs.readFileSync(inputPath, 'utf8');
// 自动跳过非 EJS 文件(如 .gitignore、README.md),或按需保留原样
if (!inputPath.endsWith('.ejs')) {
fs.copyFileSync(inputPath, outputPath);
console.log(`? Copied (non-EJS): ${path.relative(INPUT_DIR, inputPath)}`);
return;
}
const rendered = ejs.render(content, data, {
filename: inputPath, // 启用 include 支持
rmWhitespace: true // 可选:移除模板空白行
});
// 保留原始扩展名(如 index.ejs → index.html),或统一替换为 .html
const ext = path.extname(inputPath) === '.ejs'
? '.html'
: path.extname(inputPath);
const finalOutputPath = outputPath.replace(/\.ejs$/, ext);
fs.mkdirSync(path.dirname(finalOutputPath), { recursive: true });
fs.writeFileSync(finalOutputPath, rendered);
console.log(`✅ Rendered: ${path.relative(INPUT_DIR, inputPath)} → ${path.relative(OUTPUT_DIR, finalOutputPath)}`);
} catch (err) {
console.error(`❌ Failed to render ${inputPath}:`, err.message);
throw err;
}
};
const processDirectory = (dir, relativePath = '') => {
const files = fs.readdirSync(dir);
const data = loadData();
for (const file of files) {
const filePath = path.join(dir, file);
const stats = fs.statSync(filePath);
const currentRelative = relativePath ? path.join(relativePath, file) : file;
if (stats.isDirectory()) {
// 递归进入子目录,保持目录结构
processDirectory(filePath, currentRelative);
} else {
const outputPath = path.join(OUTPUT_DIR, currentRelative);
renderFile(filePath, outputPath, data);
}
}
};
// ? 启动渲染
console.log(`? Starting recursive EJS render from "${INPUT_DIR}" → "${OUTPUT_DIR}"`);
processDirectory(INPUT_DIR);
console.log('? Rendering completed successfully.');
关键特性说明:
- ✅ 真实目录结构映射:自动创建输出子目录,保留
my-template/pages/about.ejs→output/pages/about.html; - ✅ 智能文件处理:
.ejs文件被渲染,其他文件(如 CSS、图片、JSON)原样复制; - ✅ 统一数据管理:支持根目录下
data.json(如{"title": "My Site", "nav": ["Home","Blog"]}),所有模板共享上下文; - ✅ EJS 高级功能支持:启用
filename选项后,可跨子目录正确解析; - ✅ 错误防护与日志:失败时抛出明确错误,成功/跳过操作均有清晰控制台反馈。
使用前准备:
- 安装依赖:
npm install ejs; - 在
my-template/下放置data.json(可选,否则使用默认数据); - 将
.ejs后缀用于需渲染的文件(如index.ejs,layout.ejs),其余保持原扩展名; - 运行脚本:
node render.js。
该方案无外部依赖、零配置开箱即用,比寻找不稳定 fork 更安全可靠——它不是“替代 CLI”,而是以工程思维补足 CLI 的能力边界。你完全可将其封装为 npm script(如 "scripts": {"build:templates": "node render.js"}),无缝集成进现代前端工作流。











