在Stackoverflow上面看到一个问题
Client side + Server side templating, feels wrong to me, how to optimize?
对于客户端,服务端同时使用Jade模板引擎怎么优化呢?其实问题作者的原意是如何同时使用,而且能够让代码保持分离,同时易于维护。基于我对模板引擎的理解,以及以往的使用经验,其实这个问题并不难,于是我回答了一下,简单地做了一个英文的教程。搜索以下国内的文章,似乎没有一些简单的教程,可能会对好像我这样的菜鸟造成学习上的困扰,于是就把答案转译成中文。分享以下,也欢迎大家同时提供更好的编码经验,分享一下。
毫无疑问,Jade是一个非常不错的模板引擎,尽管它也有一些Bug,不过你提issue,应该很快就会被解决掉的。而且它的语法基于HAML,甚至更优,提高效率的东西因此值得我们去尝试使用。
在阅读本文之前,我假定你已经使用过其他的模板引擎,或者已经会使用Server端的模板引擎。如果不懂的话,要折腾一下,应该很容易理解的。那么在把Server端和Client端的Jade结合前,我们先尝试把单纯客户端的Jade模板渲染,做一遍。
首先我们要下载Jade到本地
git clone https://github.com/visionmedia/jade.git
接着我们需要
$ make jade.js
事实上,jade的项目下,已经有这两个文件了:jade.js 或者 jade.min.js。这个时候,我们只是需要把这两个文件放到我们喜欢的路径上,然后让浏览器加载他。
第三步,看看我的demo,我加载了jade以及jquery,而且你还看到我加载了一段jade模板代码进来。其中item是一个变量,我们就利用jade模板引擎的api使用方式,逐步地把这个模板渲染出啦。
代码中已经把基本的html元素去掉了,因此自行加上 : <script type='text/javascript' language='javascript' src="lib/jquery-1.8.2.min.js"></script> <script type='text/javascript' language='javascript' src="lib/jade/jade.js"></script> <script type='template' id='test'> ul li hello world li #{item} li #{item} li #{item} //这个是jade的模板引擎代码 我们将会渲染三个item变量出来,值都是 "this is an item" </script> <script> var compileText = $("#test").text();//获取我们要编译的模板文本,数据类型是string,注意缩进 console.log( typeof( compileText ) ); var fn = jade.compile( compileText , { layout : false } );//利用jade编译模板文本 var out = fn( { item : "this is an item " } );//编译好之后,我们执行该函数,把我们要传的变量,渲染的数据传入 console.log( out );//最后输出最终编译的结果 $("body").append( out );//利用javascript渲染到前端页面上 </script>
最终看到的结果是
hello world this is item this is item this is item
就是上面简单的几个步骤就可以把我们需要渲染的东西渲染出来了。很简单吧。那么疑问就来了,要是我们服务端要使用jade,客户端也是使用jade,岂不是有语法冲突?(试想一下,服务端使用jade,在服务端就把模板渲染好了,客户端上面拿到的是已经编译好的代码)
这个时候我们就必须要解决冲突的问题。还好。jade早就提供了这套解决方案了。这里要记住,服务端有服务端的渲染,客户端有客户端的渲染。下面来看看例子:
index.jade
!!!5 html head title hello world body ul#list script#list-template(type='template') |- for( var i in data ) | li(class='list') \#{ data[i].name } |- }
很明显,上面是服务端需要渲染的jade文件,代码应该很容易看懂了。和上面的例子一样,我们在上面要渲染的模板是id 为 list-template里面的内容。不过写法似乎有点不一样 :-)。首先缩进相同的情况下,在代码的左边都加上了 “|” 。
加上”|”的原因是,为了避免和server端的jade模板代码造成混淆,不然可能会报错,或者无法正常去到客户端渲染。
其次,你会看到在#{ data[i].name }的前面,加上了”\”符号,实质上同样是为了转义,不被污染。
下面再看javascript的代码
index.js
/* you javascript code */ var compileText = $('#list-template').text(); var compile = jade.compile( compileText , { layout : false } ); var data = [{ "name" : "Ben" } , {"name" : "Jack" } , {"name" : "Rose" }]; var outputText = compile( data ); $("#list").append( outputText );
这下很容易理解了吧。最后输出的结果就是
Ben Jack Rose
小提示:其实在调试的时候有jade语法错误提示,但是依然挺麻烦的。如果确实遇到jade模板服务端,客户端混淆的时候,不妨尝试一下先在纯html页面中,载入jade,写一些测试代码,然后让jade进行渲染。保证到需要编译的文本,是字符串,而且语法符合编译的要求。这样就没有问题了。
更新部分:
上面的内容基本上围绕服务端,以及客户端同时使用Jade的情况。那么如果单纯在客户端使用Jade呢?其实也是可以的。不过写法有点不一样。这里再从更新部分给大家展示一下。
<script id='list-template' type='template'> - var type = ['爱情','生活'];//此后还要加入颜色分类 - for( var i in data ){ li.question-list-button( data-id='#{ data[i].id }' ) .icon img( src='#{ data[i].icon }' ) .message h3.no-return #{ data[i].title } p( class='message-panel no-return' ) #{ ( 235 - data[i].id ) + '天,' +data[i].use_times+'人测过' } p( class='type-panel no-return' ) span #{ type[data[i].type] } - } </script>
大家可以看到,script标签内就是Jade模板的代码了。对于这里的缩进是有要求的。不然你的编译会出错。提示缩进不正确。正确的缩进如上面展示的代码所述。在script里面,逻辑代码全部靠左,不留可白的地方。对于变量,逻辑,循环等的特殊语句需要在前面加上横杠,这个就好像上面Jade Server Side & Client Side的Example一样了。注意的地方就是靠左以及不留空。估计我现在这样表述或许会有点不清,不过在实际写代码时,你注意一下就可以了。:-)
20130720更新部分:
今天遇到另外一个场景,就是在pyjade下,服务端以及客户端同时使用jade。此前采用的方式是直接在HTML文件里面添加一个script标签,然后写入脚本。但是使用pyjade的时候发现同样的语法会报错,暂时没有办法解决(因为这里可能是pyjade本身存在问题,也可能是我的代码没有针对pyjade写对。这里还有待研究。后来想想,我在这里边要写的jade模板其实还是不少的,全部塞进HTML文件里面似乎也不太适合。这里不如就采用一种方法,让模板文件分开管理,然后采用异步加载的方式将这些文件引入进来。
(下面是一个示例)于是我就省下了写script标签的功夫,直接把这些模板写入list-template.txt文件中。文件中的内容如下:
- for( var i in listdata ){
li #{ listdata[i].name } - }
然后在javascript中则是这样引入的:
<script id=’list-template’ type=’text/javascript’>
var request_template_dir = ‘…’;
var listdata = [{ “name” : “Ben” } , {“name” : “Jack” } , {“name” : “Rose” }];//需要全局变量才可以使用template渲染
$(function (){
$.get( request_template_dir + “list-template.txt”,function ( template_string ){
console.log( template_string );
var compileText = template_string;
console.log( typeof( compileText ) );
var compile = jade.compile( compileText , { layout : false } );
var outputText = compile( listdata );
console.log( outputText );
$(“#option_list”).append( outputText );
});
});
</script>
这样就顺利地把模板渲染出来了。这里有一点是非常值得注意的,就是你会看到我的渲染数据使用的是一个全局变量。这点非常重要,假设这个listdata写在闭包里面的话,渲染的时候会出现 listdata is not define的错误。这里的话大家稍微想多一层就会明白为什么会这样。结合以前的例子,渲染的时候,我们的模板是直接写在HTML中的,如果使用模板引擎进行渲染的时候,会直接把渲染的数据暴露在全局变量中,让操作得以正常运行,这里只是在写脚本的时候,定义好这是全局变量。因此避免出现未定义变量的情况。
其实本文并没有什么技术含量,仅仅是一个小教程以及解决一些小疑惑。jade的强大还远远不止这些呢。不过上面已经含括了挺多的功能啦,具体的语法还需要你亲自去探索。
其实最近我的个人网站也在偷偷地上线啦,www.cashlee.me 上面会属于我的开源项目,以及一些想法的实验。同时我也会对一些服务封装一些api出来,提供给大家使用。希望大家喜欢。当然我也会长期维护,放更多有趣的东西上去提供给大家玩,或者自己玩。