
本文详解如何使用 javascript 的 `string.prototype.replace()` 配合回调函数,精准匹配多行属性块(如 `@#@ ... @#@` 包裹内容),并将其动态转换为多个 `{% set ... %}` 模板语句。
在前端模板预处理或构建时,常需将自定义标记语法(如 @#@ attr="val" class="foo" @#@)转换为目标模板引擎(如 Nunjucks)可识别的语法。直接用正则捕获整个属性块后进行结构化解析,是高效且可控的方案。
核心思路是:用正则匹配 @#@ 包裹的任意属性字符串 → 在 replace 的回调函数中解析该字符串 → 拆分每个 key="value" 对 → 逐个生成 {% set key="value" %} 语句。
以下是完整、健壮的实现代码:
function convertAtHashBlocks(content) {
const regex = /@#@\s*([\s\S]*?)\s*@#@/gim; // 使用 [\s\S] 真正匹配多行(替代 . 不跨行缺陷)
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="q-promo-container promo--hidden"
expires="2023-04-30T17:00:00Z"@#@
{% include "promo-banner.njk" %}`;
console.log(convertAtHashBlocks(input));⚠️ 关键注意事项:
立即学习“Java免费学习笔记(深入)”;
- 原正则 ([a-zA-Z0-9-_/\.=:"'\s]+) 存在严重缺陷:. 在字符类中不表示“任意字符”,且未支持换行;应改用 [\s\S] 或 [^]* 实现真正多行捕获;
- 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 回调模式,是驾驭复杂文本转换的必备技能。










