ES6 模板字符串

无论是前端还是后端,都存在着模板引擎。模板引擎有着丰富的表现力,使得我们可以根据不同的数据渲染出不同的页面结构以及内容。在es6中引入了模板字符串,使得我们可以在字符串中嵌入变量、表达式以及可以直接写多行字符串。另外,模板字符串还支持嵌套使用。这些特性,一些模板引擎基本的功能,我们可以用es6模板字符串来实现。

for 循环

  • 一种比较直接的方式是:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
${
(()=>{
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循环来实现。

  • 对以上的方法稍加优化,我们可以得到如下的代码:
1
2
3
4
5
6
7
8
9
10
11
12
${
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需要我们自己去实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");

// Don't escape special characters in the template.
s += templateData[i];
}
return s;
}

从安全角度来看,这个 SaferHTML 非常脆弱。在 HTML 中,不同的地方需要用不同的方式去转义,SaferHTML 并没有做到。
另外一点是,这个方法只适用于没有嵌套使用模板字符串的情况,如果嵌套使用,那么一些不该被转义的字符串页会被转义。在这种情况下,我们还是老老实实地对每一处改转义的地方进行转义.

首先,需要编写一个转义函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const reUnescapedHtml = /[&<>"'`]/g; // 需要被转码的字符
const reHasUnescapedHtml = RegExp(reUnescapedHtml.source); // 测试是否有unescaped字符时不需要 'g'
const htmlEscapes = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'`': '&#96;'
};
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