如何在TypeScript中实现支持嵌套数据与函数的简单模板引擎?
实现极简模板引擎:支持嵌套数据与自定义函数调用
核心问题拆解
你当前的实现只能处理单层属性访问,无法解析嵌套路径(如profile.name)和函数调用逻辑。要满足需求,需要解决两个核心问题:
- 解析多层嵌套的属性路径
- 识别并执行带参数的自定义函数
具体实现方案
完整代码实现
export class TemplateEngine { public static render(template, data, functions = {}) { // 匹配{{}}内的所有表达式,兼容换行和空格 return template.replace(/{{\s*([\s\S]+?)\s*}}/g, (_, expr) => { // 判断是否为函数调用(包含()) const funcCallMatch = expr.match(/^(\w+)\((.*)\)$/); if (funcCallMatch) { const [_, funcName, argsStr] = funcCallMatch; const targetFunc = functions[funcName]; if (!targetFunc) return ''; // 解析参数:区分字符串字面量和属性路径 const args = argsStr.split(/\s*,\s*/).map(arg => { // 处理单/双引号包裹的字符串 const strLiteralMatch = arg.match(/^(['"])(.*)\1$/); if (strLiteralMatch) { return strLiteralMatch[2]; } // 处理属性路径参数 return TemplateEngine.resolvePath(data, arg.trim()); }); return targetFunc(...args); } // 处理嵌套属性访问 return TemplateEngine.resolvePath(data, expr.trim()); }); } // 工具方法:解析嵌套属性路径 private static resolvePath(obj, path) { return path.split('.').reduce((current, key) => { return current && current[key] !== undefined ? current[key] : ''; }, obj); } }
关键逻辑说明
- 正则匹配优化:使用
/{{\s*([\s\S]+?)\s*}}/g匹配表达式,兼容表达式内的空格和换行,同时精准捕获内容。 - 嵌套属性解析:通过
resolvePath方法,将profile.name这类路径拆分为数组,逐层从数据对象中取值,处理多层嵌套。 - 函数调用处理:
- 用正则识别函数调用格式(如
formatDate(...)),提取函数名和参数字符串 - 拆分参数时,区分单/双引号包裹的字符串字面量和属性路径参数
- 从传入的
functions对象中获取目标函数,执行并返回结果
- 用正则识别函数调用格式(如
测试验证
使用你提供的示例测试:
// 假设已引入moment库 TemplateEngine.render( 'Hello {{profile.name}}, the following date {{formatDate(event.date, \'dd.mm.yyyy\')}} is free.', { profile: { name: 'Tom' }, event: { date: 1682349765 } }, { formatDate: (input, format) => moment.unix(input).format(format) } ); // 输出结果:Hello Tom, the following date 24.04.2023 is free.
扩展支持文本处理函数
你可以直接在functions参数中添加所需的文本处理函数,比如:
// 示例扩展函数 const textFunctions = { uppercase: (str) => str.toUpperCase(), substring: (str, start, end) => str.substring(start, end), trim: (str) => str.trim() }; // 使用示例 TemplateEngine.render( 'Name: {{uppercase(profile.name)}}, Trimmed: {{trim(profile.nickname)}}', { profile: { name: 'tom', nickname: ' TomCat ' } }, textFunctions ); // 输出:Name: TOM, Trimmed: TomCat
内容的提问来源于stack exchange,提问作者perotom
相关产品推荐
相关产品推荐

