Skip to content

四火的唠叨

一个纯正程序员的啰嗦

Menu
  • 所有文章
  • About Me
  • 关于四火
  • 旅行映像
  • 独立游戏
  • 资源链接
Menu

模板引擎随谈

Posted on 09/19/201410/08/2024 by 四火

template engine

模板引擎是为了解耦而产生的,从编程范型的角度来说,写模板属于 “声明式(Imperative)编程”。JSP 大概是最早接触也是最基础的模板引擎,本来写 Servlet 嘛,一大堆一大堆的 print,实在是没有任何结构性可言,然后 JSP 出现,先被处理成实质为 Servlet 的 Java 文件,编译以后变成 class,接着一样执行。所以本质是编译型的模板引擎,当然模板引擎也有解释型或者二者混合的。通常说来编译型的执行效率要高得多。只要是和显示相关的编程语言,都会发展出一套或者 N 套模板引擎,用得多了觉得很多情况下都大同小异。

几年前我在工作中折腾过一段时间的服务端模板引擎,最早遗留系统使用的 Velocity,后来我们实现的时候用了 FreeMarker,因为后者功能更强大,IDE 支持也更好,对于后者的 macro(宏),实在是不知怎么讲,功能上它当然是一个强大的武器,但是没控制好就会让代码写得功能不清,或者干脆很难看懂。在搞性能调优的时候,到后来不动大刀已经没有什么可以值得改进的地方了。遂眼光瞄到了 FreeMarker 上面,我们拿 profiler 的工具检查出来模板引擎的解释执行耗费了大量的时间,而且其中的模板缓存命中率很低,公司里面有一个团队为此专门改了 FreeMarker 的代码,性能好像有 20% 的提高。

很多人搞 web 开始阶段都是自由生长的,或者说野蛮生长,完全没有章法,凭借着搜索引擎加试错大法,因此方法往往都不正统。我也一样。在我知道专门的模板以前,我已经在粗暴地实现类似的事情了,让一个 DIV 不可见(display=none),然后里面变化的地方用占位符标识,在 Ajax 获得数据以后把占位符替换成真正的文字,然后显示出来——这不就是一最土鳖的模板么?后来开始接触到一些前端模板引擎,Mustache 是最早接触的,我不知道 {{ }} 这样的记号是不是从它开始的,然后是 Handlebars,其实它用的也是 Mustache 的引擎。Underscore.js 是值得推荐的模板引擎,性能非常出色,而且语法和 JSP 差不多。AngularJS 的模板是我最喜欢的形式(下面我列出了一个官网上面的例子),因为直接融合进 HTML 里面了,减少了生硬的特殊格式标签,可以给既有 DOM 对象增加属性,也可以通过 directive 方式自定义 DOM。模板引擎怎么演进而来的,又是怎么从后端移到前端来的,其实都因一个 “解耦”,这个过程我在 《MVC 框架的映射和解耦》以及 《Web 页面的聚合技术》里面都有部分介绍。

<ul>
  <li ng-repeat="phone in phones">
    {{phone.name}}
    <p>{{phone.snippet}}</p>
  </li>
</ul>

关于常见几款前端模板的比较,这里有一篇文章。HTML5 用新标签的方式收录了模板,这里有一篇文章介绍。另外,这里有一个有趣的帖子,作者在入门 Node.js 的时候选模板,很多人在讨论 Jade,它最有意思的地方是如果打开普通的没有代码辅助的记事本文件,它的编写效率真得高出好多,而且没有烦人的括号、尖括号之类的标记符号,不知道你怎么看。对于性能的横向比较,在 JSPerf 上面有人做了一个完整的列表,可以打开页面后立即测试。

关于模板引擎的原理解析,推荐一篇文章 《高性能 JavaScript 模板引擎原理解析》,里面提到了 “高性能” 模板引擎的原理,这也是现在越来越多的 JavaScript 模板引擎的设计思路,尽量把工作放到预编译阶段去,生成函数以后,原始的模板就不再使用了,后面每次需要渲染的时候调用这个函数传入参数就可以了。

通过一个小小的例子,可以看到模板引擎的工作原理,这里拿 Handlerbars 举例:

<table>
    {{#each users}}
    <tr>
        <td>
            {{this.name}}
        </td>
        <td>
            {{this.age}}
        </td>
    </tr>
    {{/each}}
</table>

对于这样一段简单的模板,调用语句是:

var func = Handlebars.compile(document.getElementById("template").innerHTML);
var result = func({
	users : [
		{
			name : "A",
			age : 10
		},
		{
			name : "B",
			age : 20
		}
	]
});
console.log(result);

接着动态生成了这样的 Function:

this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
  var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;

function program1(depth0,data) {
  
  var buffer = "", stack1;
  buffer += "\n	<tr>\n		<td>"
    + escapeExpression(((stack1 = depth0.name),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
    + "</td>\n		<td>"
    + escapeExpression(((stack1 = depth0.age),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
    + "</td>\n	</tr>\n	";
  return buffer;
  }

  buffer += "\n<table>\n	";
  stack1 = helpers.each.call(depth0, depth0.users, {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
  if(stack1 || stack1 === 0) { buffer += stack1; }
  buffer += "\n</table>\n";
  return buffer;

其实代码并不难理解,这里的 each 就是通过内置的工具方法 helpers.each 来实现的,执行总的来说就是递归调用(第 9、11 行),如果 stack1 还是方法就继续调用,否则就直接转码(escapeExpression)显示。最终拼接成字符串输出。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

×Scan to share with WeChat

你可能也喜欢看:

  1. JavaScript 实现继承的几种方式
  2. CSS 也面向对象
  3. Function Invocation Patterns
  4. A page widgetization practice
  5. 借助 AngularJS 写优雅的代码

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

订阅·联系

四火,啰嗦的程序员一枚,现居西雅图

Amazon Google Groovy Hadoop Haskell Java JavaScript LeetCode Oracle Spark 互联网 亚马逊 前端 华为 历史 同步 团队 图解笔记 基础设施 工作 工作流 工具 工程师 应用系统 异步 微博 思考 技术 数据库 曼联 测试 生活 眼界 程序员 管理 系统设计 缓存 编程范型 美股 英语 西雅图 设计 问题 面向对象 面试

分类

  • Algorithm and Data Structure (30)
  • Concurrency and Asynchronization (6)
  • System Architecture and Design (43)
  • Distributed System (18)
  • Tools Frameworks and Libs (13)
  • Storage and Data Access (8)
  • Front-end Development (33)
  • Programming Languages and Paradigms (55)
  • Testing and Quality Assurance (4)
  • Network and Communication (6)
  • Authentication and Authorization (6)
  • Automation and Operation Excellence (13)
  • Machine Learning and Artificial Intelligence (6)
  • Product Design (7)
  • Hiring and Interviews (14)
  • Project and Team Management (14)
  • Engineering Culture (17)
  • Critical Thinking (25)
  • Career Growth (57)
  • Life Experience and Thoughts (45)

推荐文章

  • 聊一聊分布式系统中的时间
  • 谈谈分布式锁
  • 常见分布式系统设计图解(汇总)
  • 系统设计中的快速估算技巧
  • 从链表存在环的问题说起
  • 技术面试中,什么样的问题才是好问题?
  • 从物理时钟到逻辑时钟
  • 近期面试观摩的一些思考
  • RSA 背后的算法
  • 谈谈 Ops(汇总 + 最终篇):工具和实践
  • 不要让业务牵着鼻子走
  • 倔强的程序员
  • 谈谈微信的信息流
  • 评审的艺术——谈谈现实中的代码评审
  • Blog 安全问题小记
  • 求第 K 个数的问题
  • 一些前端框架的比较(下)——Ember.js 和 React
  • 一些前端框架的比较(上)——GWT、AngularJS 和 Backbone.js
  • 工作流系统的设计
  • Spark 的性能调优
  • “残酷” 的事实
  • 七年工作,几个故事
  • 从 Java 和 JavaScript 来学习 Haskell 和 Groovy(汇总)
  • 一道随机数题目的求解
  • 层次
  • Dynamo 的实现技术和去中心化
  • 也谈谈全栈工程师
  • 多重继承的演变
  • 编程范型:工具的选择
  • GWT 初体验
  • java.util.concurrent 并发包诸类概览
  • 从 DCL 的对象安全发布谈起
  • 不同团队的困惑
  • 不适合 Hadoop 解决的问题
  • 留心那些潜在的系统设计问题
  • 再谈大楼扔鸡蛋的问题
  • 几种华丽无比的开发方式
  • 我眼中的工程师文化
  • 观点的碰撞
  • 谈谈盗版软件问题
  • 对几个软件开发传统观点的质疑和反驳
  • MVC 框架的映射和解耦
  • 编程的未来
  • DAO 的演进
  • 致那些自嘲码农的苦逼程序员
  • Java 多线程发展简史
  • 珍爱生命,远离微博
  • 网站性能优化的三重境界
  • OSCache 框架源码解析
  • “ 你不适合做程序员”
  • 画圆画方的故事

近期评论

  • Ticket: TRANSACTION 1.922915 BTC. Go to withdrawal >> https://yandex.com/poll/enter/BXidu5Ewa8hnAFoFznqSi9?hs=20bd550f65c6e03103876b28cabc4da6& on 倔强的程序员
  • panshenlian.com on 初涉 ML Workflow 系统:Kubeflow Pipelines、Flyte 和 Metaflow
  • panzhixiang on 关于近期求职的近况和思考
  • Anonymous on 闲聊投资:亲自体验和护城河
  • 四火 on 关于近期求职的近况和思考
  • YC on 关于近期求职的近况和思考
  • mafulong on 常见分布式基础设施系统设计图解(四):分布式工作流系统
  • 四火 on 常见分布式基础设施系统设计图解(八):分布式键值存储系统
  • Anonymous on 我裸辞了
  • https://umlcn.com on 资源链接
© 2025 四火的唠叨 | Powered by Minimalist Blog WordPress Theme