英文原文
: Client-Side Templating
在浏览器中使用模板是一个日渐热门的趋势。将服务端的逻辑应用到客户端上,还有越来越多的类MVC模式(模型-视图-控制器:model-view-controller)的使用都使得在浏览器中“模板”的角色越来越重要。在过去,“模板”从来都是服务端的事情,但事实上在客户端开发中,模板的作用是非常强大又具有表现力的。
为什么要使用模板?
大体上来说,借助模板是一种能很好地将视图(views)中标记和逻辑分开的方法,还能将代码的重用性和可维护性最大化。如果使用的是语法与最终所得结果很相近的语言(比如HTML),你就能又快又好地把任务完成了。虽然模板可以用来输出任何形式的文本,但由于我们想要讨论的客户端开发是有关于HTML的,所以在这篇文章里,我们还是以HTML作为例子。
现在的动态应用中,客户端常常需要频繁地刷新界面。这个效果可以通过服务端将HTML片段插入到客户端的文档中。这样做的话,服务器要能支持传送HTML的片段(与之相对:传送完整的页面)。还有就是,作为一个要处理这些标记片段的客户端的开发者,你应该会想能完全控制你的模板。而模板引擎(Smarty)、流量(Velocity)还有ASP这些服务器端的内容你都不用了解,也不用管那些“面条式代码”(spaghetti code):例如在HTML文档里是不是出现的臭名昭著的<?或者<%。
那么现在来看看客户端模板吧。
第一印象
对初学者而言,理解“模板”的含义很重要,foldoc(免费在线计算机词典)中的解释是:模板是一种文档,不过文档中有形参,再通过模板处理系统的特定语法用实参代替形参。
让我们来看看最基本的模板长什么样子:
<h1>{{title}}</h1><ul>
{{#names}}<li>{{name}}</li>
{{/
names}}</ul>
如果你写过HTML,那么你一定很熟悉上面的代码。上文的HTML中有一些占位符。这些占位符将会被真实的数据取代。例如这个对象:
var data =
{
"
title
":
"
Story
"
,
"
names
"
: [{
"
name
":
"
Tarzan
"
},{
"
name
":
"
Jane
"
}]}
把数据和模板结合起来,就会得到下面的HTML代码:
<h1>Story</h1><ul><li>Tarzan</li><li>Jane</ul></ul>
将模板和数据分离开来对于维护HTML来说是一件好事。比如说,如果想要更改标签或者添加类(class)就只需要更改模板就可以了。另外,对于需要迭代出现的元素(比如<li>),程序员只需要写一次就好了。
模板引擎
模板的语法是根据你需要的模板引擎来决定的(例如:占位符{{title}})。引擎是负责分析模板,用提供的数据替换占位符(变量、函数、循环等等)。
有些模板引擎看起来没有什么逻辑性。这指的不是在模板中只能插入简单的占位符,而是说智能标签(intelligent tags)方面的特性很少(比如数组迭代器,条件渲染等等)。有些引擎就有很多特性和很好的可扩展性。关于这一点就不在这展开讲了,你需要问问自己,在模板中你是否需要、需要多少逻辑。
每个模板引擎都有自己的API,不过通常你都能找到像render()和compile()这样的方法。渲染的过程就是将真正的数据放入模板然后呈现出来。也就是说,渲染就是用真正的数据替代了占位符。如果在此期间木板上有什么逻辑,就会被执行。编译模板指的是解析模板,然后将它转换成一个JavaScript函数。模板中的逻辑都会被解释为纯JS(plain JavaScript),给定的数据会被传入这些JS函数中,这么做可以最大程度地优化HTML。
Mustache实例
上文中的例子可以借助模板引擎实现,例如使用了
Mustache模板语法的
mustache.js。关于这种语法更多信息,我会在后面告诉你的。现在先来看看下面的JS代码能得到什么效果:
var template =
'
<h1>{{title}}</h1><ul>{{#names}}<li>{{name}}</li>{{/names}}</ul>
'
;
var data = {
"
title
":
"
Story
",
"
names
": [{
"
name
":
"
Tarzan
"}, {
"
name
":
"
Jane
"
}]};
var result = Mustache.render(template, data);
现在我们需要在页面上显示模板,你需要写这么一行代码:
document.body.innerHTML = result;
第一个客户端模板就完成了!在代码文件中加入下面这句,你就可以试一试上面的例子了,或者看下在线演示
<script src=
"
https://raw.github.com/janl/mustache.js/master/mustache.js
"></script>
组织模板
如果你和我一样,不喜欢HTML文档里出现很长的内容,既造成了阅读的困难还增加了维护的负担。理想情况下,我们可以把模板分开维护,既能享受模板的语法高亮的便利,又能保证HTML的可读性。
但事情总不会十全十美的。如果一个项目中要使用非常多的模板,出于避免过多Ajax请求而影响性能的原因,我们不希望这么多文件被分开加载下来。
场景1:脚本标签
常见的解决方案就是把所有的模板直接放在
<scrpit>标签中,
<script>标签的可选类型要稍作更改,比如改成
type=”type/template”(浏览器在渲染或解析时会将这个属性忽略)。
<script id=
"
myTemplate
" type=
"
text/x-handlebars-template
"><h1>{{title}}</h1><ul>
{{#names}}<li>{{name}}</li>
{{/
names}}</ul></script>
这样的做,你就可以把所有的模板都放在HTML文档中,避免了额外的Ajax请求。
script标签中的内容会后面被JavaScript当做模板来使用。请看下面的代码,这次我们用的是
Handlebars模板引擎再结合一些jQuery,模板就用刚刚的里的。也可以直接看在线演示
var template = $(
'
#myTemplate
'
).html();
var compiledTemplate =
Handlebars.compile(template);
var result = compiledTemplate(data);
最终效果和上文的Mustache例子是一样的。Handlebars也可以使用Mustache格式的模板,所以在这里我们就用一样的模板了。不过要注意,它们之间还是有一个很重要的区别:Handlebars是先得到一个中间结果,再通过这个中间值得到HTML的。它先是将模板编译成一个JS函数(称之为compiledTemplate),然后数据再被传入这个函数中执行,再返回最终结果。
场景2:预编译模板
虽然说将渲染模板包装在一个方法里看起来要方便多了,但是将编译和渲染分开也有显而易见的优点。最重要的是,分开以后,可以把编译放在服务器端完成。我们可以在服务器上执行JS代码(比如使用Node),有些模板引擎支持这样的预编译。
我们可以用一个JS文档(叫它comiled.js吧)将多个预编译好的文件放在一起。这个文件的内容看起来可能是这样的:
var myTemplates =
{templateA: function() { ….},templateB: function() { ….};templateC: function() { ….};};
然后在应用中,我们只需要将数据传入这些预编译好的模板中:
var result = myTemplates.templateB(data);
这个方法远比上文中讨论过的将所有的模板放在
<script type=”text/javascript”>中要好,客户端会忽略编译过程。取决于你的应用套件(application stack),这个解决方式并不一定很难实现,我们会在下文看到它具体的实现。
Node.js示例
任何模板预编译脚本至少要满足下面的要求:
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。