JavaScript 轻量级模板引擎:原理、缓存与性能优化

作者:霁月时间:2026-10-29 11:48:32浏览:109

本文介绍了一个由John Resig编写的轻量级JavaScript模板引擎。该引擎支持模板缓存与预编译功能,能显著提升渲染速度。文章详细解析了其基于Function构造器的实现原理,并探讨了利用text/html类型脚本隐藏模板代码的技巧,以及通过字符串静态替换提升执行效率的性能优化策略。

轻量级JavaScript模板引擎介绍

我在开发JavaScript应用时,一直依赖一个小型工具,它被证明极具实用价值。这是一个极简的模板函数,具备执行速度快、支持缓存机制以及易于上手的特点。接下来,我将分享在使用该工具过程中积累的一些实用技巧。

以下是该模板函数的具体代码实现(更精简的版本可参考即将出版的《Secrets of the JavaScript Ninja》一书):

模板引擎核心代码实现


// 简单JavaScript模板引擎
// John Resig - http://ejohn.org/ - MIT Licensed
(function(){
 var cache = {};
 
 this.tmpl = function tmpl(str, data){
  // 判断我们是否已经有这么一个模板,或者我们需要载入模板
  // 并保证把结果保存到缓存中。
  var fn = !/\W/.test(str) ?
   cache[str] = cache[str] ||
    tmpl(document.getElementById(str).innerHTML) :
   
   // 生成一个可重用的函数,用于提供模板生成功能
   // (它会被记录到缓存内).
   new Function("obj",
    "var p=[],print=function(){p.push.apply(p,arguments);};" +
    
    // 通过with(){}把数据作为本地变量引入
    "with(obj){p.push('" +
    
    // 把模板转换未纯javascript代码
    str
     .replace(/[\r\t\n]/g, " ")
     .split("<%").join("\t")
     .replace(/((^|%>)[^\t]*)'/g, "$1\r")
     .replace(/\t=(.*?)%>/g, "',$1,'")
     .split("\t").join("');")
     .split("%>").join("p.push('")
     .split("\r").join("\\'")
   + "');}return p.join('');");
  
  // 给用户提供一些基本的柯里化功能
  return data ? fn( data ) : fn;
 };
})();

模板语法与内嵌脚本技巧

你的模板代码结构大致如下(这并非强制格式,但我个人更倾向于这种写法):

 


<script type="text/html" id="item_tmpl">
 <div id="<%=id%>" class="<%=(i % 2 == 1 ? " even" : "")%>">
  <div class="grid_1 alpha right">
   <img class="righted" src="<%=profile_image_url%>"/>
  </div>
  <div class="grid_6 omega contents">
   <p><b><a href="/<%=from_user%>"><%=from_user%></a>:</b> <%=text%></p>
  </div>
 </div>
</script>

此外,你也可以选择将脚本直接内嵌到页面中:


<script type="text/html" id="user_tmpl">
 <% for ( var i = 0; i < users.length; i++ ) { %>
  <li><a href="<%=users[i].url%>"><%=users[i].name%></a></li>
 <% } %>
</script>

技巧提示:将脚本嵌入页面时,若将其content-type设置为未知类型(例如本例中的text/html),浏览器将无法识别并忽略执行该脚本,同时搜索引擎爬虫和屏幕阅读器也会忽略它。这是一种非常有效的代码伪装手段,可将模板逻辑安全地嵌入页面。我偏爱这种快速且随性的技术方案,仅需一两个小型模板,即可构建出轻量且高效的Web应用。

模板调用与预编译优化

在脚本中,你可以按如下方式使用该模板:


var results = document.getElementById("results");
results.innerHTML = tmpl("item_tmpl", dataObject);

你可以预先编译模板结果以便后续复用。如果调用模板函数时仅传入一个ID参数(或模板字符串),它将返回一个预编译好的函数,供后续直接调用:


var show_user = tmpl("item_tmpl"), html = "";
for ( var i = 0; i < users.length; i++ ) {
 html += show_user( users[i] );
}

性能优化与字符串替换策略

这是目前最无奈的做法,涉及代码的解析与转换——你可能并不喜欢这种实现。但它包含了我偏爱的一项技术:在字符串中进行静态搜索和替换时,使用split("match").join("replace")的执行效率更高。虽然这看起来不够直观,但在现代浏览器中确实有效(注:下一版Firefox将大幅提升replace(/match/g,"replace")的性能,因此未来这种长表达式可能不再必要)。

请放松享受它——我对代码中的突变感到好奇。尽管它很简单,但仍有许多事情可以用它来做。

作者最新文章