嵌套模板中同名字段通过作用域隔离避免覆盖:递归前提取子结构并暂存,用querySelector精准定位子模板,repeatNode单独处理各层,确保每层只操作自身map。
mm)怎么避免覆盖?外层和内层 JSON 都有 mm,直接顺序替换必然冲突——先替外层的,再进递归时内层 mm 已被抹掉。关键不是“谁先谁后”,而是**作用域隔离**。
实际做法是:递归前先提取并暂存所有子结构(cc、dd 等 class 名对应的节点),用正则或 DOM 查询精准定位子模板,再用 repeatNode 单独处理它们,最后才替换当前层级的字符串键值。这样每个递归调用都只看到自己那一层的 map,mm 不会跨层污染。
node.querySelector('.cc') 必须存在且唯一,否则 subHtml 插入位置错乱subNode.outerHTML.replace(/s/g, ' ') 统一空白符,否则 tmp.replace(subTpl, subHtml) 容易因换行/缩进不匹配失败node 做 innerHTML 修改,它要反复用于生成 subTpl
repeatNode 函数为什么必须接收 DOM 节点而非 HTML 字符串?因为只有真实 DOM 节点才能可靠执行 querySelector 和遍历子结构。如果传入字符串,.querySelector('.cc') 会返回 null,后续递归直接崩;而用 document.createElement 解析字符串又引入额外解析开销和兼容性风险(比如 IE 不支持某些 HTML5 标签)。
实操中常见错误是把模板写成字符串然后 document.createElement('div').innerHTML = template,这会导致 outerHTML 包含多余的 wrapper div,破坏原始结构层级。
立即学习“前端免费学习笔记(深入)”;
node 是从文档中取的真实节点,比如 document.getElementById('test').children[0]
arr 参数必须是数组,哪怕只有一项也要包成 [map],否则 for...in 遍历时会把字符串当字符数组处理element.innerHTML,不能直接 appendChild
每层递归都做一次 outerHTML 字符串拷贝 + 正则全局替换,O(n²) 复杂度。10 层嵌套 + 每层 20 个字段,可能触发浏览器重排卡顿。
真正拖慢的不是循环本身,而是字符串拼接和 replace 的重复扫描。尤其当模板里有大量空白符或注释时,tmp.replace(/s/g, ' ') 这步反而增加负担。
console.time() 测单次 repeatNode 耗时,超过 16ms(1帧)就要优化DOMParser 解析模板字符串为临时文档,用 cloneNode(true) 复制节点树,再遍历修改 textContent 或 setAttribute,避开字符串操作aa、bb)可预编译正则: const re_aa = /{aa}/g,比运行时构造快 3 倍innerHTML 直接拼接,而坚持递归 DOM 节点?因为模板里混用了列表(cc 数组)、键值对(d1、d2)和深层对象(d3 数组里的 x/y),三者渲染逻辑不同:列表要循环生成多个副本,键值对是一对一替换,对象要再递归。硬拼字符串根本没法区分这些语义。
更隐蔽的问题是 XSS —— 如果 mm 值含 <script>alert(1)</script>,用 innerHTML 直接插入就执行了;而递归方案至少能控制在文本节点层面做 textContent 设置(虽然当前示例没这么做,但架构上留了口子)。
tmp.replace(re, map[j]) 是最脆弱环节,生产环境必须加 escapeHtml(map[j])
{n} : {v} : {mm} 这种组合式占位符无法被简单正则捕获,实际项目应约定只允许单层 {key},复杂表达式交给 JS 逻辑预处理template 标签 + content.cloneNode(true),比手写递归更稳定