如何让模板替换函数实现递归?是否会出现无限递归?
支持数组递归渲染的模板替换函数实现
实现逻辑
要让模板替换函数支持数组递归渲染,核心是在匹配到数组类型的变量时,遍历数组每一项,用当前项作为数据源递归调用模板函数,最后把所有递归结果拼接起来替换原占位符。具体步骤:
- 用正则匹配所有
%variable%(支持嵌套属性如%user.name%)格式的占位符 - 对每个占位符对应的变量,从数据源中取值:
- 如果是数组:遍历数组元素,对每个元素递归执行模板替换,将所有结果拼接成字符串
- 如果是普通值(字符串/数字等):直接替换占位符
- 如果是嵌套对象:解析嵌套属性路径,取出对应值替换
- 加入循环引用防护,避免无限递归
无限递归风险
正常使用下不会触发无限递归,但如果数据源存在循环引用(比如数组元素引用了父数组,或对象引用链形成闭环),就会导致递归无法终止。解决办法是在递归过程中记录已处理过的对象/数组,遇到重复引用时直接跳过。
代码实现
function renderTemplate(templateStr, data, processed = new WeakSet()) { // 匹配所有%xxx%或%xxx.yyy%格式的占位符 return templateStr.replace(/%([\w.]+)%/g, (match, key) => { // 解析嵌套属性路径 let value = data; const keys = key.split('.'); for (const k of keys) { value = value?.[k]; if (value === undefined) break; } if (value === undefined) return match; // 找不到值则保留原占位符 // 处理数组:递归渲染每个元素后拼接 if (Array.isArray(value)) { if (processed.has(value)) return ''; // 跳过已处理的数组,防止循环引用 processed.add(value); return value.map(item => renderTemplate(templateStr, item, processed)).join(''); } // 处理对象:防止循环引用,这里只处理非数组对象 if (typeof value === 'object' && value !== null) { if (processed.has(value)) return ''; processed.add(value); return String(value); } // 普通值直接转换为字符串 return String(value); }); } // 实际使用示例 const invoiceMasterTpl = ` <div class="invoice"> <h3>发票编号:%invoiceId%</h3> <div class="item-list"> %items% </div> </div> `; const invoiceItemTpl = ` <div class="item"> <span>商品:%productName%</span> <span>数量:%count%</span> <span>单价:%price%</span> </div> `; const invoiceData = { invoiceId: 'INV-2024-007', items: [ { productName: '无线耳机', count: 1, price: 299 }, { productName: '充电底座', count: 2, price: 59 } ] }; // 先渲染所有商品项,再渲染主发票模板 const renderedItems = invoiceData.items.map(item => renderTemplate(invoiceItemTpl, item)).join(''); const finalInvoice = renderTemplate(invoiceMasterTpl, { ...invoiceData, items: renderedItems }); console.log(finalInvoice);
补充说明
- 示例中先单独渲染商品项模板,再把结果传入主模板。如果想让函数自动识别数组对应的子模板,可以扩展函数参数,比如传入一个模板映射表(如
{ items: invoiceItemTpl }),让函数在处理数组时自动使用对应子模板递归 - 使用
WeakSet记录已处理对象,不会影响垃圾回收,是处理循环引用的高效方式
内容的提问来源于stack exchange,提问作者Shawn
相关产品推荐
相关产品推荐

