blog icon indicating copy to clipboard operation
blog copied to clipboard

ES6 模板字符串

Open shaozj opened this issue 8 years ago • 0 comments

无论是前端还是后端,都存在着模板引擎。模板引擎有着丰富的表现力,使得我们可以根据不同的数据渲染出不同的页面结构以及内容。在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, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;");
	
    // 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 = {
        '&': '&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

shaozj avatar Dec 18 '16 13:12 shaozj