`
netcome
  • 浏览: 482455 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

20行实现javascript模板引擎

阅读更多

20行实现javascript模板引擎

我仍然在用AbsurdJS预处理器写javascript。起初,这只是一个CSS预处理器,后来我把它扩展为CSS/HTML预处理器。最近,它可以实现javascript到CSS/HTML的转换,因为它可以作为模板引擎来生成HTML。比如,可以用数据填充HTML模板。

然后,我就想写一个简单的模板引擎可以完美地与我当前的开发工作相配合。AbsurdJS主要是作为nodejs模块来发布的,但是它也可以作为客户端使用。有了这种想法,我意识到我不能利用现有的模板引擎。因为现在的大多数模板引擎只是基于nodejs,很难把他们复制到浏览器来使用。我需要一个体积小的用原生JS写的模板引擎。我曾经拜读过John Resig的一篇文章JavaScript Micro-Templating。这好像就是我所需要的。我把里面的代码做了些许变动,使之缩减为20行。我想这脚本的运行机制是非常有趣的。本文中,我一步步地重新创建一个模板引擎,然后你就会体会到来自John的伟大创意。

我们以下面的代码作为开始吧:

 

  1. var TemplateEngine = function(tpl, data) {
  2. // magic here ...
  3. }
  4. var template = '<p>Hello, my name is <%name%>. I\'m <%age%> years old.</p>';
  5. console.log(TemplateEngine(template, {
  6. name: "Krasimir",
  7. age: 29
  8. }));

一个简单的函数,来处理我们的模板与数据对象。

你可能会猜想到,我们最终想要达到的结果就是下面的样子: 

 

  1. <p>Hello, my name is Krasimir. I'm 29 years old.</p>

首先我们必须处理模板内部的动态语法,然后我们用传递给模板引擎的真实数据来替换这些动态语法。我决定利用正则表达式来实现。正则表达式不是我的强项,所以你可以留言建议给我一个更好的正则表达式。 

 

  1. var re = /<%([^%>]+)?%>/g;

这样,我们会捕获到分别以“%”开始与结束的分组。“g”(global全局)表示我们得到的不是一个,而是全部匹配到的。采用正则表达式的exec方法可以把匹配到分组的以数组的形式表现: 

 

  1. var re = /<%([^%>]+)?%>/g; var match = re.exec(tpl); 
  1. console.log(match);

结果: 

 

  1. [
  2. "<%name%>",
  3. " name ",
  4. index: 21,
  5. input:
  6. "<p>Hello, my name is <%name%>. I\'m <%age%> years old.</p>"
  7. ]

但是我们只得到了一个,再来改善一下: 

 

  1. var TemplateEngine = function(tpl, data) {
  2. var re = /<%([^%>]+)?%>/g;
  3. while(match = re.exec(tpl)) {
  4. tpl = tpl.replace(match[0], data[match[1]])
  5. }
  6. return tpl;
  7. }

OK,我们的最初目标达到了,但这远远不够。这只能容易获取到data['property']。但在实践中,我们可能遇到复杂的嵌套对象。比如: 

 

  1. {
  2. name: "Krasimir Tsonev",
  3. profile: { age: 29 }
  4. }

我们先前所做的工作就这样失效了!

那我们就分析一下其他的情况。比如,我们有个这样的模板: 

 

  1. var template = '<p>Hello, my name is <%this.name%>. I\'m <%this.profile.age%> years old.</p>';

再或者我们可能还见过这样的模板: 

 

  1. var template =
  2. 'My skills:' +
  3. '<%for(var index in this.skills) {%>' +
  4. '<a href=""><%this.skills[index]%></a>' +
  5. '<%}%>';

这该怎么办呢? John采用了 new Function 语法实现——可以从字符串来创建函数。

那我们先来熟悉一下这种语法。看一个简单的例子: 

 

  1. var fn = new Function("arg", "console.log(arg + 1);");
  2. fn(2); // outputs 3

上述代码创建的函数fn等价于: 

 

  1. function fn(arg){
  2. console.log(arg+1);
  3. }
  4. fn(2) // outputs 3

这样我们可以自定义函数,其参数与函数体可以来自简单的字符串。而我们所需要的方法应该能够返回为最终的编译模板。就像这样: 

 

  1. return
  2. "<p>Hello, my name is " +
  3. this.name +
  4. ". I\'m " +
  5. this.profile.age +
  6. " years old.</p>";

而对于 

 

  1. var template =
  2. 'My skills:' +
  3. '<%for(var index in this.skills) {%>' +
  4. '<a href=""><%this.skills[index]%></a>' +
  5. '<%}%>';

我们所需要的应该是这样: 

 

  1. return
  2. 'My skills:' +
  3. for(var index in this.skills) { +
  4. '<a href="">' +
  5. this.skills[index] +
  6. '</a>' +
  7. }

当然我们所设想的会产生语法错误,那我们可以改变一下: 

 

  1. var r = [];
  2. r.push('My skills:');
  3. for(var index in this.skills) {
  4. r.push('<a href="">');
  5. r.push(this.skills[index]);
  6. r.push('</a>');
  7. }
  8. return r.join('');

有了预想结果,下一步就是分别处理每一行来产生自定义函数。

在进行处理每一行的时候,我们应该考虑到以下问题:

  • 引号的转义,否则产生的脚本不可用
  • <% %>里面的字符不应该被当做字符串处理 
  1. var TemplateEngine = function(tpl, data) {
  2. var re = /<%([^%>]+)?%>/g,
  3. code = 'var r=[];\n',
  4. cursor = 0;
  5. var add = function(line) {
  6. // 引号转义,将"替换为\"放入定义的函数体
  7. code += 'r.push("' + line.replace(/"/g, '\\"') + '");\n';
  8. }
  9. //匹配到<% %>
  10. while(match = re.exec(tpl)) {
  11. // <% %>之前当做字符串放入函数体
  12. add(tpl.slice(cursor, match.index));
  13. // <% %>中间部分
  14. add(match[1]);
  15. //迭代处理<% %>后面部分
  16. cursor = match.index + match[0].length;
  17. }
  18. add(tpl.substr(cursor, tpl.length - cursor));
  19. code += 'return r.join("");'; // <-- return the result
  20. console.log(code);
  21. return tpl;
  22. }
  23. var template = '<p>Hello, my name is <%this.name%>. I\'m <%this.profile.age%> years old.</p>';
  24. console.log(TemplateEngine(template, {
  25. name: "Krasimir Tsonev",
  26. profile: { age: 29 }
  27. }));

考虑到if/else等JS语句,我们再做优化: 

 

  1. var TemplateEngine = function(html, options) {
  2. var re = /<%([^%>]+)?%>/g, reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g, code = 'var r=[];\n', cursor = 0;
  3. var add = function(line, js) {
  4. js? (code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n') :
  5. (code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : '');
  6. return add;
  7. }
  8. while(match = re.exec(html)) {
  9. add(html.slice(cursor, match.index))(match[1], true);
  10. cursor = match.index + match[0].length;
  11. }
  12. add(html.substr(cursor, html.length - cursor));
  13. code += 'return r.join("");';
  14. return new Function(code.replace(/[\r\t\n]/g, '')).apply(options);
  15. }

终极目标实现!!

详见最终版本

原文作者:http://krasimirtsonev.com/blog/article/Javascript-template-engine-in-just-20-line

2
1
分享到:
评论
1 楼 xyfwjk 2013-12-24  
不明觉厉

相关推荐

    只有 20 行的 JavaScript 模板引擎实例详解

    在这个只有20行的JavaScript模板引擎实例中,我们将深入理解如何实现这一功能。 首先,模板引擎的核心任务是找到模板字符串中的标识片段,即`&lt;%...%&gt;`,并用提供的数据对象替换这些片段。在给定的代码中,使用了一...

    tstemplate一个基于模板字符串的模板引擎的最简实现只适用于node环境

    “tstemplate一个基于模板字符串的模板引擎的最简实现只适用于node环境” 这个标题揭示了我们讨论的核心是名为“tstemplate”的模板引擎,它利用了JavaScript的模板字符串功能,并且是针对Node.js运行环境设计的。...

    JavaScript模板引擎Mustache.zip

    基于javascript 实现的模板引擎,类似于 Microsoft’s jQuery template plugin,但更简单易用! 标签:Mustache

    基于JavaScript的模板引擎

    JavaScript模板引擎是前端开发中的重要工具,用于将数据与HTML结构分离,实现动态内容渲染。在Web应用中,为了提高代码可维护性和可扩展性,开发者通常会使用模板引擎来生成视图层的HTML。这些模板引擎允许我们用...

    JavaScript的mustache模板引擎的一个极其快速和小的子实现

    在给定的标题"JavaScript的mustache模板引擎的一个极其快速和小的子实现"中,我们可以推断这里提到的是一个轻量级的、高效的Mustache实现,可能是针对特定场景进行了优化,例如可能去除了某些非必要的功能,以达到更...

    基于jQuery开发的javascript模板引擎-jTemplates

    **基于jQuery开发的JavaScript模板引擎 - jTemplates** jTemplates是一个基于jQuery的JavaScript模板引擎,它允许开发者使用HTML模板来动态渲染数据,极大地提高了Web应用的界面更新效率和用户体验。通过将数据与...

    arttemplate高性能javascript模板引擎.docx

    传统的JavaScript模板引擎在出现错误时难以定位问题所在,而artTemplate通过记录模板中的换行符信息,能够在遇到错误时立即抛出异常并指出模板对应的行号,方便开发者快速定位并解决问题。 - **渲染错误**:这类...

    laytpl JavaScript模板引擎 V1.1

    laytpl是一款颠覆性的JavaScript模板引擎,它用巧妙的实现方式,将自身的体积变得小巧玲珑,不仅性能接近极致,并且还具备传统js引擎的几乎所有功能。所有的变身魔法都由不到2KB的代码创造,laytpl视图用最轻量的...

    JavaScript模板引擎 - Elapse

    JavaScript模板引擎Elapse是Web开发中的一个重要工具,它主要用于生成动态HTML内容,使得开发者能够将数据和视图逻辑分离,提高代码的可读性和可维护性。模板引擎的工作原理是通过解析预先定义好的模板语法,然后...

    简单的前端模板引擎能实现最基本的数据渲染

    例如,Mustache和Handlebars是两个流行的JavaScript模板引擎,它们提供了丰富的语法结构,如`{{#if}}...{{/if}}`(条件判断)和`{{#each}}...{{/each}}`(循环遍历数组)。 总的来说,前端模板引擎是前端开发中不可...

    JavaScript模板引擎原理与用法详解.docx

    JavaScript模板引擎是一种在JavaScript中处理数据和HTML组合的工具,它的主要目的是将数据逻辑与页面展示分离,提高代码的可维护性和复用性。在Web开发中,它扮演着一个预处理器的角色,允许开发者编写类似于HTML的...

    JavaScript模板引擎Velocity.js.zip

    Velocity 是基于Java的模板引擎,广泛应用在阿里集 体各个子公司。Velocity模板适用于大量模板使用的场景,支持复杂的逻辑运算,包含 基本数据类型、变量赋值和函数等功能。Velocity.js 支持 Node.js 和浏览器环境。...

    laytpl-JavaScript模板引擎 v1.1

    laytpl是一款颠覆性的JavaScript模板引擎,它用巧妙的实现方式,将自身的体积变得小巧玲珑,不仅性能接近极致,并且还具备传统前端引擎的几乎所有功能。所有的变身魔法都由不到1KB的代码创造,这仿佛是一场革命,又...

    PowJS基于DOMTree的JavaScript模板引擎

    PowJS与其他JavaScript模板引擎(如Handlebars、EJS、Mustache等)相比,其特色在于DOM Tree的直接操作,这可能会提供更快的渲染速度和更好的浏览器兼容性。然而,每个模板引擎都有其适用场景,选择哪种引擎应根据...

    Tempos高效的Javascript模板引擎

    Tempos是一款高效的JavaScript模板引擎,专为前端开发者设计,用于快速构建动态HTML页面。在Web开发中,模板引擎扮演着至关重要的角色,它允许开发者将数据和视图逻辑分离,使得代码更加模块化,更易于维护。Tempos...

    高性能JavaScript模板引擎实现原理详解

    《高性能JavaScript模板引擎实现原理详解》 JavaScript模板引擎在Web开发中的地位日益凸显,尤其是在JavaScript MVC架构中扮演着关键角色。模板引擎允许开发者将数据和视图逻辑分离,提高代码的可读性和复用性。...

    淘宝前端UED资料 Juicer 一个Javascript模板引擎的实现和优化

    Juicer是一个JavaScript模板引擎,它是由淘宝前端UED团队开发的,其主要目的是为了优化前端的代码结构和提升页面渲染的性能。模板引擎的核心作用是将数据和模板相结合,生成HTML页面,它能够有效地将数据的处理逻辑...

    laytpl-JavaScript模板引擎 v1.1-源码.zip

    总的来说,laytpl-JavaScript模板引擎v1.1的源码分析能够让我们深入理解前端模板引擎的内部运作,提升JavaScript开发技能,同时也为我们提供了自定义和优化模板引擎的可能性。通过阅读和学习源码,开发者可以更好地...

    templatejs的fis编译插件一款javascript模板引擎

    Template.js就是这样一个JavaScript模板引擎,它被设计用来帮助开发者快速、高效地实现动态内容的展示。 **template.js介绍** template.js是一款轻量级的JavaScript模板引擎,它提供了简洁的语法和高效的执行效率...

Global site tag (gtag.js) - Google Analytics