• 一步一步实现一个前端模板引擎


    不要重复发明轮子,这是我听到最多的一句话,而且现在有很多优秀的模板引擎:handlebar、ejs、artTemplate...那么为什么还要自己实现一个呢?原因不外乎有两个,
    一来是手痒,二来是满足一点小小的虚荣心:看,模板引擎我也会,简单!感觉非常优(zhuang)秀(bi)。

    既然是自己动手,那么网上的教程肯定先放一边,突然有点耗子啃南瓜——无从下口的感觉...

    一切从需求出发

    从后台拿到数据,拼接成字符串放在页面中,这是我们初入前端时常要做的工作,特别是遇到结构稍微复杂的页面,光拼接字符串都能搞得你一脸懵逼、二脸懵逼,终于
    有一天遇到模板引擎,一边惊为天人,一边暗自骂自己傻逼。那么,今天我们动手实现的模板引擎,就从那最初的那一天开始吧!

    字符串模板

    话说有天接到需求,需要将一组JSON数据,渲染到页面中。如下所示:

    var data = [
        { text: 'text1' ,status:'done' },
        { text: 'text2' ,status:'pending' }
    ];
    
    var tpl = '<ul>'+
        '<%for(var i = 0, len = data.length; i < len; i++) {%>'+
        '<li class="<%= data[i].status%>"><span><%= data[i].text%></span></li> '+
        '<%}%>'+
        '</ul>';
    
    

    最初的渲染函数

    机智如我自然想到用函数来循环。。。

    var render = function(data) {
        var tmp = '';
        tmp += '<ul>';
        for(var i = 0, len = data.length; i < len; i++) {
            tmp += '<li class="'+ data[i].status +'">'+data[i].text+'</li>';
        }
        tmp += '</ul>';
        return tmp;
    };
    
    

    目前来讲,我们返回了渲染好的字符串,而且看来工作的很顺利。但如果将字符串增加点内容,这个函数就GG思密达了。由此看来,我们需要把字符串模板单独提取出来,然后再
    进行数据渲染。

    牛B的Function

    我们用的最多的就是 function 关键字了,但对于 function 的爸爸 Function 却有点陌生,那么 Function 究竟哪里流弊呢?红宝石书不是建议我们不要用 Function吗?
    其实,在JS中,但我们使用 function 声明函数的时候,JS会自动调用 Function 来生成实例。并且,Function 为我们提供了更强大的武器——动态函数。

    语法
    var function_name = new Function(arg1, arg2, ..., argN, function_body)

    等同于

    var function_name = function(arg1,..., argN) {function_body}

    于是,我们就有了一把强力的武器,将动态的字符串,放在动态的函数中执行了。

    提取字符串构造函数体

    有了前面的知识基础,这一步,我们就要把 tpl 中的字符串,变成 render 的函数体。这就需要另一把武器——正则表达式。利用它,来找到需要渲染数据的位置。

    var reg = /<%([sS]+?)%>/g

    然后,通过 replace 方法替换 reg 找到的位置,构造成函数体!

    var template = function(tpl) {
        var reg = /<%([sS]+?)%>/g;
        // index 用来记录替换的位置
        var index = 0;
        // 需要构造的函数体(一步一步和上面的render函数对比)
        var func_body = "var tmp = '';";
        func_body += "tmp += '";
    
        tpl.replace(reg, function(match, val, offset, str){
            // 每一次匹配到后,截取当前匹配位置和上一次匹配完成后位置之间的字符串
            func_body += tpl.substring(index, offset);
    
            // 根据 %= 判断如何进行拼接函数体
            if(match.indexOf('%=') < 0) {
                func_body +="';" + val + ";tmp += '";
            } else {
                func_body += "' + " + val.replace('=', '').trim() + "+'";
            }
    
            // 完成一次match,改变index 的值
            index = offset + match.length;
            return index;
        });
    
        // 完成所有匹配后,将剩下的字符串加入
        func_body += tpl.substring(index);
        // 返回 tmp
        func_body += "';return tmp;";
        return func_body;
    };
    
    

    现在,只要我们调用 template 函数,就会返回如 render 的函数体类似的字符串。要使template 函数返回的字符串运行起来,就要用到 Function 了。

    var tmpEngine = function (tpl, data) {
        // 返回字符串函数体
        var func_body = template(tpl);
        // 通过 Function 运行
        return new Function('data', func_body).call(null, data);
    };
    

    于是,我们调用 tmpEngine, 就可以得到经过数据渲染后的字符串了。

    var m = render(tpl2, data2);
    
    console.log('m:' +m);
    
    // m: <ul><li class="done"><span>text1</span></li> <li class="pending"><span>text2</span></li> </ul>
    
    

    至此,我们的模板引擎的功能层面已经完成,可以愉快的玩耍了。但是!还有很多优化工作等待着推进,这里罗列几条,周末再战:

    • 特殊字符转义,业务可能需要输出html代码,减少XSS攻击
    • 数据为空时的处理
    • 性能

    ......

  • 相关阅读:
    python中字符串 s[ : 4]是什么意思?字符串切片
    python找不到模块的问题解决方法(有空钻研一下具体的原因)
    解决CUDA error: no kernel image is available for execution on the device 测试cuda是否可用
    执行 shell 报错 source:not found,一行注释解决
    【项目】项目137
    【项目】项目134
    【项目】项目135
    【项目】项目139
    【项目】项目142
    【项目】项目141
  • 原文地址:https://www.cnblogs.com/fayin/p/6803654.html
Copyright © 2020-2023  润新知