简单的前端渲染模板实现

国庆节过去第一天,有点懒,但一想到今年的法定节假日都已经过完,慢慢就燃起奋斗欲望。今天碰巧看到网络上一些高手博客写着渲染模板教程,就做个随笔记录吧!

渲染模板简单的说,就是将一些数据,字符串加载到几个的变量当中。

var tpl = 'Hei, my name is <%name%>, and I\'m <%age%> years old.';

js数据加载方式

var data = {
    "name": "Barret Lee",
    "age": "20"
};

var result = tplEngine(tpl, data);

以上使用方式,相比大家都很熟悉,目前成熟的渲染模板也有很多,包括一些mvc框架avaron ,angular,vue等都自带前端渲染模板。所以大家也知道模板具有维护方便,代码清晰,版本迭代都很方便。缺点就是seo 等,不过大家做项目时候,关于框架选择,模板选择基本上都是别人搞好,我们都是学着用就可以了。

所以我们就上面代码,做个js编译,我们要把<%name%>识别出来,然后将js数据写进去,所以我们要使用正则表达式。

我的名字叫做<%name%>

上面代码使用正则获取<%name%>变量,然后将data中的name值替换,精简版js模板已经完成。嗯,哪怕这只是简单的字符串替换。

由于时间关系,暂时这样,明天有空继续补上。

------------------------2017-10-10---------------------

继续昨天代码,当对象为对象,字符串替换方式就失灵了,我们换一种方式去探索

<%name%> <%info.age%>

上面是得到变量之后,根据<%%>符号判断,然后返回一个js对象值,是的,上面返回两次,本来就有两个变量~~

根据上面思路,我们继续,假若存在for循环,我们要怎么解析它的代码?将for循环转成字符串返回?嗯我们试试

let result = tmpl.replace(/<%([^%>]+)?%>/g,function(s0,s1){
      return 'Posts: ' + 
       'for(var i = 0; i < post.length; i++) {'
         '' + post[i].exper + ' }'
    })

这段代码明显不行,为何?他会直接输出该字符串,我们要的是得到多个a标签和内容,所以我们需要分开保存,js数组上场了。

let result = [];
            result.push('Post');
            result.push('for(var i = 0; i < post.length; i++) {');
            result.push('');
            result.push(post[i].exper);
            result.push('');
            result.push('}');
上面使用数组装载for循环的语句和一些字符串,看起来完美,但事情没那么简单,这样子的数组都是字符串,无法转换,我们换一下

var r = [];
r.push('Posts: ' );
for(var i = 0; i < post.length; i++) {
    r.push('');
    r.push(post[i].exper);
    r.push('');
}
这样子看来没问题了,那么我们怎么解析运行呢?这里要用到new Function()对象实例,可能很多人对function熟悉,但其实内部还是要经过new Function得到一个实例。我们可以使用另外一种方式创建函数

var function_name = new function(arg1, arg2, ..., argN, function_body)
在上面的形式中,每个 arg 都是一个参数,最后一个参数是函数主体(要执行的代码)。这些参数必须是字符串。

是不是很熟悉?再看例子

function sayHi(sName, sMessage) {
  alert("Hello " + sName + sMessage);
}
上面是普通的不能在普通的函数声明,那么new Function的

var sayHi 
= 
new Function("sName", "sMessage", "alert(\"Hello \" + sName + sMessage);");

是不是很像?恩,一般情况下,我们很少使用new Function去声明函数,因为麻烦。对其感兴趣可以看下这个链接  http://www.w3school.com.cn/js/pro_js_functions_function_object.asp

回到正题来,我们要用new Function构建一个函数来运行我们的数组,我们先来看下直接运行相关代码样子

var fn = new Function("data", 
    "var r = []; for(var i in data){ r.push(data[i]); } return r.join(' ')");
fn({"name": "barretlee", "age": "20"}); // barretlee 20

new function例子完整输出

<% for(var i = 0; i < post.length; i++) {+ <% post[i].expert %> + <% } %>

fn函数中传入一个对象,返回一个字符串集合,join() 方法用于把数组中的所有元素放入一个字符串。元素是通过指定的分隔符进行分隔的。所以思路就有了,我们可以把逻辑部分和非逻辑部分的代码链接成一个字符串,然后利用类似fn的函数直接编译代码。为了能够识别所有元素,我们要使用exec代替replace。

exec() 方法用于检索字符串中的正则表达式的匹配,返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。

以下为w3cShool解释

说明
exec() 方法的功能非常强大,它是一个通用的方法,而且使用起来也比 test() 方法以及支持正则表达式的 String 对象的方法更为复杂。
如果 exec() 找到了匹配的文本,则返回一个结果数组。否则,返回 null。此数组的第 0 个元素是与正则表达式相匹配的文本,第 1 个元素是与 RegExpObject 的第 1 个子表达式相匹配的文本(如果有的话),第 2 个元素是与 RegExpObject 的第 2 个子表达式相匹配的文本(如果有的话),以此类推。除了数组元素和 length 属性之外,exec() 方法还返回两个属性。index 属性声明的是匹配文本的第一个字符的位置。input 属性则存放的是被检索的字符串 string。我们可以看得出,在调用非全局的 RegExp 对象的 exec() 方法时,返回的数组与调用方法 String.match() 返回的数组是相同的。
但是,当 RegExpObject 是一个全局正则表达式时,exec() 的行为就稍微复杂一些。它会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string。当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置为匹配文本的最后一个字符的下一个位置。这就是说,您可以通过反复调用 exec() 方法来遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。

http://www.w3school.com.cn/jsref/jsref_exec_regexp.asp

var reg = /<%([^%>]+)?%>/g;
            var tpl = 'Hei, my name is <%name%>, and I\'m <%age%> years old.';
            var match = reg.exec(tpl);
            console.log(match);

var reg = /<%([^%>]+)?%>/g;
while(match = reg.exec(tpl)) {
    console.log(match);
}

对比下两者区别?第一种只会识别<%name%>,那么<%age%>呢?提示:请注意,无论 RegExpObject 是否是全局模式,exec() 都会把完整的细节添加到它返回的数组中。这就是 exec() 与 String.match() 的不同之处,后者在全局模式下返回的信息要少得多。因此我们可以这么说,在循环中反复地调用 exec() 方法是唯一一种获得全局模式的完整模式匹配信息的方法。

<% for(var i = 0; i < this.posts.length; i++) {%> <% this.posts[i].expert %> <% } %>

还有点没搞懂,明天继续




参考链接:http://www.cnblogs.com/hustskyking/p/principle-of-javascript-template.html

你可能感兴趣的:(前端渲染模板)