首页

js实现一个简单的html模板解析器

kkcode
2019-05-04  阅读 1266

一个最基本的模板解析器,需要有什么功能呢?

  1. 读取变量值!
  2. 解析模板语句

按照这个思路,我们编写一个简单的解析器,需求如下:

  1. 读值: <%= 变量名 %>
  2. 语句支持: <% if( type == 1 ){ %> qdgithub! <%}%>

html模板如下:

我叫:<%= name %> <br/>
<% if(type == 1){ %>
    状态: <%= type %>
<% } %>复制代码

如果有数据{name: 'qdgithub' type: 1},则需要输出: 我叫:qdgithub 状态:1

按照上面需求,我们需要做什么呢?因为考虑到要在模板内执行脚本, 所以,最直接的办法, 就是把模板翻译为 js

变量

思路:

  1. 通过正则匹配找出<%...%>的模块,进行处理。
  2. 把所有匹配出来的内容,拼接成字符串。
  3. 然后通过 new Function 生成可执行函数。

要想把解析成功,首先需要把数据传入到模板解析器函数中。

// 数据
var data = {name:"qdgithub", type: 1};

// 模板解析器应该有个生成变量声明语句
var statement = '';
for(var name in data){
    statement += 'var ' + name + ' = data.' + name + ';';
}

//即:statement = 'var name = data.name;var type = data.type;'复制代码

函数

因为模板经常会是某个元素的innerHTML,其中的换行,我们直接替换掉。

<script type="text/html" id="template">
我叫:<%= name %> <br/>
<% if(type == 1){ %>
    状态: <%= type %>
<% } %>
</script>

<script>
    var html = template.innerHTML;
    // 替换掉换行:
    html = html.replace(/\s/g, " ");
</script>复制代码

考虑到, ‘xx内容<% 表达式 %>’ 这种形式比较好用正则匹配, 所以,把 ‘前面内容<%表达式%>后面内容’ 的 ‘前面’ 和 ‘后面’ 部分去掉:

// resList是最终结果,rightContent是‘后面’部分的内容。
var resList = [],rightContent = '';
html = html.replace(/(.*?)(<%.*%>)(.*?)/g,function(str,left,center,right){
    rightContent = right;
    resList.push(left);
    console.log("left:%s center:%s right:%s", left, center, right);
  return center;
});

// 正则匹配的内容将是:
// left: 我叫:
// center: <%= name %> <br/>  <% if(type == 1){ %> 状态: <%= type %> <% }%>
// right:复制代码

剩下 ‘center’ 的内容,都符合 ‘xx内容<%表达式%>’ 的格式, 所以,使用正则,编译剩下的内容:

var __TMP_LIST__ = '__TMP_LIST__',
    __TMP_DATA__ = '__TMP_DATA__';

// 需要编译的动态函数内容
var fnStr = '';

// 开始匹配表达式,匹配 'xxx<% expr %>'
html.replace(/(.*?)<%(.*?)%>/g, function(str, html, exp){
    console.log("html:%s exp:%s", html, exp);
    // html内容
    fnStr += __TMP_LIST__ + ".push('" + html + "');";

    // 表达式内容,带等号,取值,不带,则是动态函数的逻辑控制部分
    if(exp.indexOf("=") === 0){
        fnStr += __TMP_LIST__ + ".push("+ exp.slice(1) +");";
    }else{
        fnStr += exp;
    }
});复制代码

生成动态函数

// 构建编译的动态函数
var fn = new Function(__TMP_DATA__, __TMP_LIST__, statement + fnStr);

// 如果结果列表没有数据,则没有需要编译的内容
if(resList.length > 0){
    // 把需要编译的数据,结果列表,传入动态函数
    fn(data, resList);
    // 不要漏了最后一个小尾巴,上面去除 ‘前’ 和 ‘后’部分,留下的
    resList.push(rightContent);
    console.log(resList.join(""));
}else{
    console.log(html);
}复制代码
本文为作者原创文章,转载无需和我联系,但请注明转载链接。 【前端黑猫】