
本文详解如何使用 javascript 的 `String.prototype.replace()` 配合回调函数,精准匹配多行属性块(如 `@#@ … @#@` 包裹内容),并将其动态转换为多个 `{% set … %}` 模板语句。
在前端模板预处理或构建时,常需将自定义标记语法(如 @#@ attr=”val” class=”foo” @#@)转换为目标模板引擎(如 Nunjucks)可识别的语法。直接用正则捕获整个属性块后进行结构化解析,是高效且可控的方案。
核心思路是:用正则匹配 @#@ 包裹的任意属性字符串 → 在 replace 的回调函数中解析该字符串 → 拆分每个 key=”value” 对 → 逐个生成 {% set key=”value” %} 语句。
以下是完整、健壮的实现代码:
function convertAtHashBlocks(content) { const regex = /@#@s*([sS]*?)s*@#@/gim; // 使用 [sS] 真正匹配多行(替代 . 不跨行缺陷) return content.replace(regex, (match, attrsBlock) => { // 按双引号结尾 + 可选空白符分割(支持换行、缩进) const attrPairs = attrsBlock .trim() .split(/"s+/g) .filter(str => str.trim() !== ''); return attrPairs .map(pair => { // 安全提取 key="value" 格式:允许 value 中含空格,但必须以 " 结尾 const kvMatch = pair.match(/^([^=]+)=(".*?"|'.*?'|[^'"s]+)/); if (!kvMatch) return `{% set ${pair.trim()}" %}`; // 降级兜底 const [, key, value] = kvMatch; return `{% set ${key.trim()}=${value} %}`; }) .join('n'); }); } // ✅ 使用示例 const input = `@#@ visibility="hidden" @#@ {% include "grid-overlay.njk" %} @#@ contentPartial="promo/tru-report/" class="tuc-19bc10f7-e41518-0 q-promo-container promo--hidden tuc-19bc10f7-e41518-0" expires="2023-04-30T17:00:00Z"@#@ {% include "promo-banner.njk" %}`; console.log(convertAtHashBlocks(input));
⚠️ 关键注意事项:
立即学习“Java免费学习笔记(深入)”;
- 原正则 ([a-zA-Z0-9-_/.=:”‘s]+) 存在严重缺陷:. 在字符类中不表示“任意字符”,且未支持换行;应改用 [sS] 或 [^]* 实现真正多行捕获;
- split(/”s+/g) 依赖引号后紧跟空白作为分隔,对紧凑格式(如 a=”x”b=”y”)会失效;生产环境建议用更严谨的 tokenizer 或正则全局匹配 /w+s*=s*(“[^”]*”|'[^’]*’|[^s]+)/g;
- 回调函数中 match 参数包含完整匹配项,attrsBlock(即捕获组1)是去除了 @#@ 边界的原始属性块,务必 .trim() 清除首尾空白再处理;
- 若属性值含转义引号(如 “a\”b”),需增强解析逻辑,此处为教学简化,实际项目推荐使用专用解析器(如 parse-attributes 库)。
该方法兼具灵活性与可读性,适用于构建脚本、vs code 插件、或 CI 中的模板标准化流程。掌握 replace 回调模式,是驾驭复杂文本转换的必备技能。