如何用js实现一个简版的模板解析系统?

好吧,之前就实现过。。。

拍脑袋实现版

function compileTpl(tpl, data) {
    var pattern = /{%(.+)%}/g;
    return tpl.replace(pattern, function(){
        return data[arguments[1]];
    });
}

var tpl = '<div>哈哈哈,我叫{%name%}</div>';
var data = {name: '李狗蛋'};
console.log(compileTpl(tpl, data));

肯定结果长这样:

image.png


但是现实中的模板调用并非如何单调,有可能是{user.name.firstName},如果再用上面的方面就不再适用

升级版本

function compileTpl(tpl, data) {
    var pattern = /{%(.+)%}/g;
    return tpl.replace(pattern, function(){
        return eval('data.'+arguments[1]);
    });
}

var tpl = '<div>哈哈哈,我姓{%name.first%}</div>';
var data = {
    name: {
        first: '李'
    }
};
console.log(compileTpl(tpl, data));

image.png


最近在看underscore.js里面template方法的实现,依葫芦画瓢,实现了个简单版本,发现with在这里的用法很巧妙。

这句正则也很巧妙

/<%=([sS]+?)%>|<%([sS]+?)%>|$/g;

加了个$收尾,能够将replace里面会多一执行,这样就能在js代码拼接的时候做一些收尾工作

简单版本:

function template(str) {
    var regex = /<%=([sS]+?)%>|<%([sS]+?)%>|$/g;
    var source = '';
    var index = 0;
    str.replace(regex, function(express, match, jsExpress, offset, tpl) {
        if (str.slice(index, offset)) {
            source += '
__p +="' + str.slice(index, offset) + '"';
        }
        index = offset + express.length;
        if (match) {
            source += '+' + match;
        } else if (jsExpress) {
            source += '
' + jsExpress + '
';
        }
        console.log(arguments, source, match);
        return match;
    });
    source = '
with(obj || {}){' + source + '}';
    source = 'var __p=""' + source + '
return __p';
    var render = new Function('obj', source);
    console.log(render);
    return render;
}

还有一种实现是讲字符解析成节点树,有时间再看。。。