美文网首页
入门篇-如何用javascript实现一个模板引擎&转载自【蚂蚁

入门篇-如何用javascript实现一个模板引擎&转载自【蚂蚁

作者: Promise_4483 | 来源:发表于2019-05-06 15:25 被阅读0次

    模板引擎简介

    模板引擎是html渲染必不可少的工具,前端开发的同学经历了从最原始的字符串拼接、用数组push/join,发展到封装简单的string.format函数,再到功能更加强大的模板引擎,可以在模板中内嵌处理逻辑。

    传统的页面开发语言技术asp.net,php,jsp都内置了模板引擎,javascrip常见的模板引擎有jquery的tmpl插件,underscore的template函数,ejs、jade、artTemplate等,以ejs为例,模板语法如下:

    <% if (user) { %>     <h2><%= user.name %></h2> <% } %>

    模板中可以直接访问绑定的数据源变量,并且可以支持任意的javascript代码片段,如if,for循环等

    原理

    我们从需求出发,想一想把上面的模板字符串中的变量替换成数据源中的变量值,并支持嵌入的if等逻辑,需要怎样实现?能支持内嵌js代码执行,那肯定是要使用eval来编译的,只要把模板看成两部分,一部分是在百分号以内的,使用eval执行;另一部分是在百分号以外的,原样输出; 最后顺序拼接成一个完整的字符串就可以了。

    正则表达式分割字符串

    首先要把模板,按照%分隔进行拆分成多个代码片段,并按顺序存入到数组中 分割模板字符串可以用String对象的split方法,很多人不知道split方法是可以支持正则表达式的,因为我们的分隔符有两个<%和%>,所以要用正则表达式,可以同时匹配这两个字符,代码就只有一行:

    var arr=str.split(/(\<%=?|%\>)/gm);

    拆分后的代码片段生成一个数组,我们遍历一下这个数组,将代码片段分成3种类型

    1. javascript代码

    2. html片段(原样输出的字符)

    3. 用<%=%>输出的变量值

    只要按顺序拼接生成的字符串代码,最终编译执行字符串代码就可以了

    编译字符串的方法

    1. eval

    js最常见的编译字符串的方法当然是eval,可以直接在当前函数作用域将一个字符串编译成可执行代码,有一条代码规范叫做don't be eval,这是因为使用eval是用代码生成代码,会让代码更加难以看懂,并且会有一些性能损耗。

    2. new Function

    与eval功能相似,不同的是他会将传入的字符串代码编辑后生成一个function,而且可以生成函数的参数,更加方便调用,封装性隔离性更好。调用形式:new Function(args,functionBody),为了更好的可读性,我们选择new Function

    3. setTimeout/setInterval

    setTimeout/setInterval 的第一个参数,可以传入一个函数,这是最常规的用法,也可以传入一个字符串,使用字符串的时候自动调用eval执行,这个用法不太常见。

    作用域问题

    模板中肯定需要一些动态变量,就是所谓的数据源,模板编译后是生成独立函数的,作用域是隔离的,而数据源是在业务代码中,有独立的作用域,不能直接访问,那么怎么能把两个不同的作用域连接起来呢,with,只有with能做到,将指定的对象插入到当前作用域链的顶端。

    完整代码实现

    function render(tpl,data){     
        tpl=tpl.replace(/(\r|\n)/ig,"");     
        var arr=tpl.split(/(\<%=?|%\>)/gm);//拆分模板     
        var funcBody=["with(this){\r\nvar result=[];"];     
        var item,codeType;     
        codeType=0;     
        for(var i=0;i<arr.length;i++){         
            item=arr[i];         //将代码片段分为3类         
            if(item=="<%"){            
                codeType=1;           
                continue;         
            }else if(item=="<%="){            
                codeType=2;            
                continue;         
            }else if(item=="%>"){             
                codeType=0;             
                continue;         
           }          //为3类代码片段生成最终可被eval的函数体         
           if(codeType==0){ //字符             
               funcBody.push("result.push(\"");             
               funcBody.push(item);             
               funcBody.push("\");\r\n");         
           }else if(codeType==1){ //代码             
               funcBody.push(item);             
               funcBody.push("\r\n");         
           }else if (codeType==2){ //代码输出             
               funcBody.push("result.push(");             
               funcBody.push(item);             
               funcBody.push(");\r\n");         
           }     
       }     
       funcBody.push("return result.join('')\r\n}");     
       var template_func=new Function(["renderData"],funcBody.join(""));     
       return template_func.apply(data,[data]);  
    }

    测试一下

    var html=render("<% list.forEach(function (item,idx){ %>" +" <div><%=idx+1+
    '、'+item%></div>" +" <%})%>", {list:["javascript","css","node.js"]}) 
    document.write(html)

    完美运行

    我们来看一下最终编译后生成的函数是什么样子的,如下图:

    由于生成的函数是用apply调用的,template_inner.apply(data,[data]),所以函数内部的this指向传入的数据源(data变量),因此可以在模板中直接使用传入的数据源对象

    模板引擎与ES6模板字符串对比

    ES6新增了模板字符串功能,不同于普通字符串的单引号和双引号,模板字符用`符号定义,在模板字符中直接可以通过${变量名}访问当前作用域内的变量并直接输出该变量的值,并且在js文件中定义大段的html字符串时,一般是把html片段粘帖进来,包含很多换行符,而模板字符串可以直接兼容换行符,使用起来非常方便。上文中的模板,用ES6的模板字符串来实现,代码也非常精简,如下:

    var user={name:"windy"}; 
    var str=`<h2>${user.name}</h2>`

    ES6模板字符串与普通的模板引擎相比,最大区别在于开发流程的不同,业务逻辑是在js中实现的,模板只实现纯净的变量替换功能,代码与逻辑分离,比较规范易用,可维护也较好,而普通的模板引擎不仅实现了变量替换,还可以内嵌js逻辑代码,更加灵活和强大。

    作者:windyfancy
    链接:https://juejin.im/post/5ccf8e13e51d453aa44ad6c9

    原文地址:https://www.v5ant.com/details/LwjoQ4rqp.html

    相关文章

      网友评论

          本文标题:入门篇-如何用javascript实现一个模板引擎&转载自【蚂蚁

          本文链接:https://www.haomeiwen.com/subject/wotdoqtx.html