首页 > 代码库 > Javascript Template 源码解析

Javascript Template 源码解析

  之前的一篇文章,简单介绍过了javascript Template(以下简称tmpl)。其作为一个前端模板,功能强大而代码精炼。源码不长,读起来不难,但其中的想法确实很犀利。这是github地址。下面先贴出全部源码,再逐句解析。

 1 /* 2  * JavaScript Templates 2.4.1 3  * https://github.com/blueimp/JavaScript-Templates 4  * 5  * Copyright 2011, Sebastian Tschan 6  * https://blueimp.net 7  * 8  * Licensed under the MIT license: 9  * http://www.opensource.org/licenses/MIT10  *11  * Inspired by John Resig‘s JavaScript Micro-Templating:12  * http://ejohn.org/blog/javascript-micro-templating/13  */14 15 /*jslint evil: true, regexp: true, unparam: true */16 /*global document, define */17 18 (function ($) {19     "use strict";20     var tmpl = function (str, data) {21         var f = !/[^\w\-\.:]/.test(str) ? tmpl.cache[str] = tmpl.cache[str] ||22                 tmpl(tmpl.load(str)) :23                     new Function(24                         tmpl.arg + ‘,tmpl‘,25                         "var _e=tmpl.encode" + tmpl.helper + ",_s=‘" +26                             str.replace(tmpl.regexp, tmpl.func) +27                             "‘;return _s;"28                     );29         return data ? f(data, tmpl) : function (data) {30             return f(data, tmpl);31         };32     };33     tmpl.cache = {};34     tmpl.load = function (id) {35         return document.getElementById(id).innerHTML;36     };37     tmpl.regexp = /([\s‘\\])(?!(?:[^{]|\{(?!%))*%\})|(?:\{%(=|#)([\s\S]+?)%\})|(\{%)|(%\})/g;38     tmpl.func = function (s, p1, p2, p3, p4, p5) {39         if (p1) { // whitespace, quote and backspace in HTML context40             return {41                 "\n": "\\n",42                 "\r": "\\r",43                 "\t": "\\t",44                 " " : " "45             }[p1] || "\\" + p1;46         }47         if (p2) { // interpolation: {%=prop%}, or unescaped: {%#prop%}48             if (p2 === "=") {49                 return "‘+_e(" + p3 + ")+‘";50             }51             return "‘+(" + p3 + "==null?‘‘:" + p3 + ")+‘";52         }53         if (p4) { // evaluation start tag: {%54             return "‘;";55         }56         if (p5) { // evaluation end tag: %}57             return "_s+=‘";58         }59     };60     tmpl.encReg = /[<>&"‘\x00]/g;61     tmpl.encMap = {62         "<"   : "&lt;",63         ">"   : "&gt;",64         "&"   : "&amp;",65         "\""  : "&quot;",66         "‘"   : "&#39;"67     };68     tmpl.encode = function (s) {69         /*jshint eqnull:true */70         return (s == null ? "" : "" + s).replace(71             tmpl.encReg,72             function (c) {73                 return tmpl.encMap[c] || "";74             }75         );76     };77     tmpl.arg = "o";78     tmpl.helper = ",print=function(s,e){_s+=e?(s==null?‘‘:s):_e(s);}" +79         ",include=function(s,d){_s+=tmpl(s,d);}";80     if (typeof define === "function" && define.amd) {81         define(function () {82             return tmpl;83         });84     } else {85         $.tmpl = tmpl;86     }87 }(this));

 

  这个工具的目的是把模板语句转化为js语句,执行,然后输出。

     先看80行到86行,如果是基于requirejs或者seajs,tmpl对象通过define返回。如果不是,就把它放进当前环境的tmpl对象里(如果是浏览器环境,当前的环境就是window)。这种用于兼容不同引用方式的技巧在写自己的一些外部通用方法的时候,也是很有用的。

  接下来,我们从头开始。刚开始时一大堆注释,库名、license、受javascript micro-Templating启发,介绍了这些内容。从第18行开始,是库的入口。20行开始定义tmpl函数对象,该函数的形参是str,data。str可以是模板,也可以是模板元素的id。data是需要被替换的json对象。data可以不传,这时tmpl返回的是一个转化json对象的工具函数。

  第21行定义f函数,f函数是整个工具的核心。这里首先用!/[^\w\-\.:]/.test(str)检查str是否是id(这里有点不明白的是id里怎么会有:),如果不是,说明str是模板,就通过这个模板返回new Function(){}(见23行)。如果是id,先检查缓存里是否有相应的函数(缓存里的函数是要事先设置的,见该工具的说明文档),没有的话,就调用自身,传入模板元素里的innerHTML(见34行tmpl.load函数),返回形参为data的函数。可以看到,f函数最后都是一个函数,这个函数会操作data和tmpl对象,返回整个tmpl函数的结果。而这个操作的过程,都在23行生成的函数内。

     我们看一下,23行生成的函数到底做了什么。这个函数的一个形参是‘o‘(见77行),这个形参可以通过设置tmpl.arg来修改。还有一个形参是‘tmpl‘,就是tmpl对象本身。在函数的主体部分,定义了_e函数,这个是用于转义的工具函数(见68行)。还定义_s,这是最后要输出的字符串。还定义一些辅助函数(见78行),默认的辅助函数有两个,print函数用于把字符串加到_s里,可以设置字符串是否转义。include函数可以把别的模板和数据转化成字符串添加到_s。可以通过改写tmpl.helper来写一些自定义的函数,比如:tmpl.helper+=",alert=function(str){window.alert(str);}";这样alert函数就可以在模板语句里调用了。

      接着看第26行,通过str.replace把str转化为js里的合法语句。先看其中的正则部分(37行)以及相应的函数(38行),替换函数p1匹配的是一些空白字符、单引号和斜杠,且这些字符不在{%和%}内(见‘(?!(?:[^{]|\{(?!%))*%\})‘),匹配函数把将这些字符前加斜杠以实现转义。p2匹配的是‘{%=‘中的‘=‘或者是‘{%#‘中的‘#‘,如果匹配的是‘=‘,就用_e函数转义p3内的变量(在函数主体内转化后就是一个变量)。p4匹配的是‘{%‘,替换为"‘;"。p5匹配的是‘%}‘,替换为"_s+=‘";

     通过这些匹配和替换之后,函数主体就被拼凑完成了。29行就是判断是否有data参数,有的话,就转换。没有的话,就返回一个转换的工具函数。

     附:到这里,也算勉强说完。很多细节都省略了,即使想说清楚也很难有条理。终究是水准不行,慢慢练吧。。。。

Javascript Template 源码解析