underscore-analysis
underscore-analysis copied to clipboard
Underscore _.template 方法使用详解
前文 浅谈 Web 中前后端模板引擎的使用 我们简单了解了模板引擎在前后端的应用场景,本文重点深入 Underscore 的模板函数 _.template,来看看它的用法以及实现原理。
from simplest
我们从 官方文档 中最简单的例子说起。
var compiled = _.template("hello: <%= name %>");
var html = compiled({name: 'moe'}); // hello: moe
{name: 'moe'} 模拟后台请求到的接口数据,而变量 html 则为拼接成的字符串,之后便可以用 innerHTML 方法加入到页面生成 DOM。
这一切是如何做到的?我们可以打印看下 compliled 方法是个什么样子(需要去 Underscore 源码中打印)。
大概是这个样子(其实不完全准确,真实的应该还会有个 _ 参数传入,使得函数能用 Underscore 内部方法):
function(obj){
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
with(obj||{}){
__p+='hello: '+
((__t=( name ))==null?'':__t)+
'';
}
return __p;
}
仔细想想,其实就是对模板字符串进行了正则解析,将需要填入数据的位置预留出来,拼接成一个字符串,用 new Function 构造一个方法(动态执行 JavaScript 字符串),方法中有大量的字符串拼接过程,然后将数据代入这个方法,返回我们需要的 HTML 字符串。
盗用 木神 两张图,过程非常清晰。
三种模板
_.template 支持以下三种模板。
1. <% %> - to execute some code
2. <%= %> - to print some value in template
3. <%- %> - to print some values HTML escaped
<% %>
里包裹的是一些可执行的 JavaScript 语句,比如 if-else 语句,for 循环语句,等等。<%= %>
正是我们前面使用的,会打印传入数据相应的 key 的值,<%- %>
和前者相比,多了步 HTML 实体编码的过程,可以有效防止 XSS 攻击。
举个栗子:
<div></div>
<script src="underscore.js"></script>
<script type="text/template" id="tpl">
<ul class="list">
<% _.each(obj, function(e, i, a){ %>
<% if (i === 0) %>
<li><%- e.name %>
<% else if (i === a.length - 1) %>
<li class="last-item"><%= e.name %></li>
<% else %>
<li><%= e.name %></li>
<% }) %>
</ul>
</script>
<script>
// mock data
var data = [{name: "<script>"}, {name: "orange"}, {name: "peach"}];
var compiled = _.template(document.getElementById("tpl").innerHTML);
var html = compiled(data);
// console.log(html)
document.querySelector("div").innerHTML = html;
</script>
将数据用 li
标签循环展示,并且将第一个值实体编码了。
其他功能
_.template 最基础的应用就是这样。
如果你不喜欢它默认的模板风格,也可以自己定义,注意 key 必须和源码中的 key 保持一致,才能覆盖。
_.templateSettings = {
// 三种渲染模板
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
有两种方式,一种是直接修改 _.templateSettings 变量(不推荐,修改了源码中的变量)
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g
};
var template = _.template("Hello {{ name }}!");
var ans = template({name: "Mustache"});
console.log(ans); // Hello Mustache!
比较好的方法是作为 _.template 的第二个参数 settings 传入:
var settings = {
interpolate: /\{\{(.+?)\}\}/g // 覆盖 _.templateSettings.interpolate
};
var template = _.template("Hello {{ name }}!", settings);
var ans = template({name: "Mustache"});
console.log(ans); // Hello Mustache!
我们还能设定 settings.variable 指定 scope:
var template = _.template("Using 'with': <%= data.answer %>", {variable: 'data'})
var ans = template({answer: 'no'});
console.log(ans) // Using 'with': no
预编译
模板引擎一般都带有预编译功能,_.template 也不例外。
什么是预编译?有什么用?
上面的代码有两个痛点:
-
性能:模板引擎渲染的时候依赖 Function 构造器实现,Function 与 eval、setTimeout、setInterval 一样,提供了使用文本访问 javascript 解析引擎的方法,但这样执行 javascript 的性能非常低下。
-
调试:由于是动态执行字符串,若遇到错误调试器无法捕获错误源,导致模板 BUG 调试变得异常痛苦。在没有进行容错的引擎中,局部模板若因为数据异常甚至可以导致整个应用崩溃,随着模板的数目增加,维护成本将剧增。
如果我们 JavaScript 代码中直接保存 _.template 的结果,那么以上两个问题就不复存在。而 _.template(jstText).source
则保存了 _.template(jstText)
返回的方法字符串。
JST is a server-side thing, not client-side. This mean that you compile Unserscore template on server side by some server-side script and save the result in a file. Then use this file as compiled Unserscore template.
小结
关于 _.template 方法的具体实现,可以参考楼主的 underscore-1.8.3.js 源码解读全文注释版 ,全局搜索即可。
关于前端模板引擎,其实楼主也是个初学者,学习过程中搜到的资料与大家分享下,有机会一定要用下各种模板引擎然后分析下。
很好的一篇underscore.js的软文...
变量怎么放到class样式中呢?比如返回图标数组,根据不同名指定class样式显示不同的图标,谢谢
不错哦 看了之后知道循环怎么写了~~