本文介绍一种基于 Function 构造函数的安全模板变量替换方案,支持对象路径访问(如 uu.message)、备用表达式(||)、字符串字面量及 XSS 防护,避免 eval 风险并内置缓存优化。
本文介绍一种基于 `function` 构造函数的安全模板变量替换方案,支持对象路径访问(如 `uu.message`)、备用表达式(`||`)、字符串字面量及 xss 防护,避免 `eval` 风险并内置缓存优化。
在前端开发中,常需将含双大括号语法(如 {{uu.message}})的 HTML 模板字符串,按作用域对象(scope)动态渲染为实际内容。直接使用 eval 存在严重安全隐患(变量污染、执行任意代码),而手动解析表达式又难以支持 || 默认值、嵌套属性等复杂场景。以下提供一个专业、健壮且可复用的解决方案。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
✅ 核心设计原则
- 安全优先:使用 new Function(args, 'return ' + expr) 替代 eval,严格限定作用域,杜绝外部变量泄漏;
- 表达式兼容:支持 JS 表达式语法(如 uu.link || "#null"、works、"plain string");
- XSS 防护:对所有输出值默认 HTML 转义(textContent → innerHTML),防止脚本注入;
- 性能优化:通过 cache 对象缓存已编译的表达式函数,避免重复解析;
- 作用域明确:要求传入扁平化作用域对象(如 {uu, works}),而非隐式 this 或全局查找。
? 实现代码(含注释)
// 安全 HTML 转义工具(防 XSS)
const span = document.createElement('span');
const escape = (str) => {
if (str == null) return '';
span.textContent = str;
return span.innerHTML;
};
/**
* 在 HTML 字符串中替换 {{expression}} 占位符
* @param {string} html - 含 {{...}} 的原始 HTML 字符串
* @param {Object} scope - 可供表达式访问的顶层作用域对象(如 {uu, works})
* @param {Object} [cache={}] - 表达式编译缓存(内部自动维护)
* @returns {string} 渲染后的 HTML 字符串
*/
const insertReplacements = (html, scope, cache = {}) => {
// 动态生成参数列表:'uu,works'
const argNames = Object.keys(scope).join(',');
const args = '{' + argNames + '}';
return html.replace(/\{\{(.*?)\}\}/g, (_, expr) => {
// 去除首尾空格,跳过空表达式
expr = expr.trim();
if (!expr) return '';
// 缓存命中:复用已编译的 Function
if (cache[expr] !== undefined) {
return cache[expr];
}
try {
// 构造沙箱函数:仅接收 scope 中定义的变量
const fn = new Function(args, 'return ' + expr);
const value = fn(scope);
cache[expr] = escape(value);
return cache[expr];
} catch (err) {
console.warn(`Template expression error in "{{${expr}}}":`, err);
return ''; // 错误时静默返回空字符串
}
});
};
? 使用示例
const works = "It_works";
const uu = {
message: 'use this message here. <test>',
learnMore: 'learn more',
link: 'dai sit ein link',
target: '_self',
markup: '{{uu.message}} test: {{works}} <a data-cc="uu.link" target="{{uu.target}}" class="info" href="%7B%7Buu.link%20%7C%7C%20">{{uu.learnMore}}</a>'
};
// ✅ 关键:将所有变量显式传入 scope 对象
const result = insertReplacements(uu.markup, { uu, works });
console.log(result);
// 输出:
// "use this message here. <test> test: It_works <a data-cc="uu.link" target="_self" class="info" href="dai%20sit%20ein%20link">learn more</a>"</test>
⚠️ 注意事项与最佳实践
- 永远不要传入用户可控的 scope:若数据来自后端或表单,请先做白名单校验;
- HTML 插入需额外标记:当前方案默认转义所有输出。如需渲染可信 HTML(如富文本),可扩展语法(如 {{{uu.rawHtml}}}),并在 escape() 前加判断;
- 避免深层嵌套作用域:scope 应为扁平对象,不建议传入 window 或 this;
- 生产环境启用缓存:cache 参数可跨调用复用(如预编译常用模板),大幅提升性能;
- 错误处理友好:try...catch 确保单个表达式失败不影响整体渲染,并输出警告日志便于调试。
该方案平衡了灵活性、安全性与性能,适用于轻量级模板渲染、邮件内容生成、配置化 UI 组件等场景,是 eval 的现代替代方案。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










