blog
blog copied to clipboard
ES6 模板字符串
无论是前端还是后端,都存在着模板引擎。模板引擎有着丰富的表现力,使得我们可以根据不同的数据渲染出不同的页面结构以及内容。在es6中引入了模板字符串,使得我们可以在字符串中嵌入变量、表达式以及可以直接写多行字符串。另外,模板字符串还支持嵌套使用。这些特性,一些模板引擎基本的功能,我们可以用es6模板字符串来实现。
for 循环
- 一种比较直接的方式是:
${
(()=>{
let html = '';
for (let i = 0, len = blocksData.length; i < len; i++) {
let curData = blocksData[i];
let jsonData = JSON.stringify(curData);
html += `<div class="item-box block4in1 ${setClass(i)}" data-json="${eH(jsonData)}" fe-role="Widget">
<div class="item-content">
<img class="poster" src="${eH(baseUrl + curData.recommendImage)}">
<div class="item-mask"></div>
</div>
</div>`;
}
return html;
})()
}
可以看到,我们利用一个立即执行函数,在函数内写一个for循环来实现。
- 对以上的方法稍加优化,我们可以得到如下的代码:
${
blocksData.reduce((prev, next, i, arr) => {
let jsonData = JSON.stringify(next);
return prev +=
`<div class="item-box block4in1 ${setClass(i)}" data-json="${eH(jsonData)}" fe-role="Widget">
<div class="item-content">
<img class="poster" src="${eH(baseUrl + next.recommendImage)}">
<div class="item-mask"></div>
</div>
</div>`;
}, '')
}
我们利用了Array的reduce方法,从而不需要将代码封装在一个立即执行函数之中。
if else 条件判断
很容易想到用三目运算符:
${(data && data != '') ? `<div>${data}</div>` : `<div>no contnet</div>`}
如果只在存在数据时有相应的html,不存在数据时没有html,则可以这么写:
${(data && data != '') && `<div>${data}</div>`}
特殊字符转义
在模板中非常重要的一点是字符串的转义,因为涉及到安全的问题,一个简单的方式是利用标签模板:
var message = SaferHTML`<p>${bonk.sender} has sent you a bonk.</p>`;
上面的代码等价于:
var message = SaferHTML(templateData, bonk.sender);
其中, templateData 是一个不可变的字符串数组,在上例中为:
Object.freeze(["<p>", " has sent you a bonk.</p>"]
SaferHTML需要我们自己去实现:
function SaferHTML(templateData) {
var s = templateData[0];
for (var i = 1; i < arguments.length; i++) {
var arg = String(arguments[i]);
// Escape special characters in the substitution.
s += arg.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
// Don't escape special characters in the template.
s += templateData[i];
}
return s;
}
从安全角度来看,这个 SaferHTML 非常脆弱。在 HTML 中,不同的地方需要用不同的方式去转义,SaferHTML 并没有做到。
另外一点是,这个方法只适用于没有嵌套使用模板字符串的情况,如果嵌套使用,那么一些不该被转义的字符串页会被转义。在这种情况下,我们还是老老实实地对每一处改转义的地方进行转义.
首先,需要编写一个转义函数:
const reUnescapedHtml = /[&<>"'`]/g; // 需要被转码的字符
const reHasUnescapedHtml = RegExp(reUnescapedHtml.source); // 测试是否有unescaped字符时不需要 'g'
const htmlEscapes = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'`': '`'
};
const escapeHtmlChar = basePropertyOf(htmlEscapes);
function basePropertyOf(object) {
return function(key) {
return object == null ? undefined : object[key];
};
}
/**
* 转义html字符串
* @param string {String} 输入的字符串
* @return {String} 转义后的字符串
*/
function escapeHTML(string) {
return (string && reHasUnescapedHtml.test(string))
? string.replace(reUnescapedHtml, escapeHtmlChar)
: string;
}
module.exports = escapeHTML;
然后我们在每一个需要转义字符串的地方调用 escapeHTML 方法进行转义。
参考文献:
DOM based XSS Prevention Cheat Sheet
[Web 安全] 了解XSS与防范
模版字符串
i18n with tagged template strings in ECMAScript 6