一个简单template engine

大佬的github

A simple template engine,only 50 rows.

学习目的

  • 与大佬约饭有一种约会的感觉(平淡脸.jpg),偶然听从大佬写了一个简单的模板编译工具,就兴致勃勃的拿下来看看。
  • 最近看vue源码一脑袋包,在一路堵堵的情况下,也算是看懂了watcher observe dep三者的关系,接下来的render部分,忙完这阵子业务再去看把 哭脸.jpg。

模板demo

<body>
    <div id="root"></div>
    <script id="tplContent" type="text/html">
    <ul>
        <% for(var i=0; i < data.length; i++){
            var item = data[i];
            if(item.age < 30){%>
                <li>我的名字是<%=item.name%>,我的年龄是<%=item.age%></li>
            <%}else{%>
                <li>my name is <%=item.name%>,my age is a sercet.</li>
            <%}%>
        <% } %>
    </ul>
    </script>
    <script src="../build/mini-tpl.min.js"></script>
    <script>
        var data = [{ name: 'tom', age: 12 }, { name: 'lily', age: 24 }, { name: 'lucy', age: 55 }];
        var content = document.getElementById('tplContent').innerHTML;
        var result = miniTpl(content, data);
        document.getElementById('root').innerHTML = result;
    </script>
</body>
复制代码

中用特定的分割符注入代码后,就可以像写js一样写dom了 来跟踪一下这个过程

源码

var testLock = true
        function cos(e) {
            if (testLock) {
                console.log(e)
            }
        }
function factory() {
            function render(content, data) {
                data = data || {}; //
                var list = ['var tpl = "";'];
                var codeArr = transform(content);
                for (var i = 0, len = codeArr.length; i < len; i++) {
                    var item = codeArr[i];
                    if (item.type == 1) { //html标签
                        list.push(item.txt);
                    } else if (item.type == 2) { //需要赋值的语句
                        var txt = "tpl+=" + item.txt + ";";
                        list.push(txt);
                    } else {
                        var txt = 'tpl+="' + item.txt.replace(/"/g, '\\"') + '";';
                        list.push(txt);
                    }
                }
                list.push("return tpl;");
                cos(list.join("\n"))
                cos(data)
                cos(new Function("data", list.join("\n"))(data))
                return new Function("data", list.join("\n"))(data);
            }

            function transform(content) { //赋值的过程,并输出正则的切割数组
                cos(content.split(''))
                var arr = [];
                var reg = /<%([\s\S]*?)%>/g;
                var match;
                var nowIndex = 0;
                var i = 0

                while (match = reg.exec(content)) { // 匹配多少个,就push多少次*2
                    cos(match)
                    appendTxt(arr, content.substring(nowIndex, match.index)); //index匹配初始到的位置
                    var item = { //默认是标签
                        type: 1,
                        txt: match[1] //匹配的内部内容,不带<%这个的
                    };
                    if (match[1].substr(0, 1) == "=") { //如果开头是=,则表示是赋值语句
                        item.type = 2;
                        item.txt = item.txt.substr(1);
                    }
                    arr.push(item);
                    nowIndex = match.index + match[0].length;
                }
                cos(content.substr(nowIndex))
                appendTxt(arr, content.substr(nowIndex)); //将%>后面的闭合标签push进去
                cos(arr)
                return arr;
            }

            function appendTxt(list, content) { //list是一个引用类型,修改的list是传入的list的 ,content也是
                content = content.replace(/\r?\n/g, "\\n");
                list.push({
                    txt: content
                });
            }
            return render;
        }
        (function (root, factory) {
            if (typeof define === "function" && define.amd) {
                define(factory);
            } else if (typeof exports === "object") {
                var mo = factory();
                mo.__esModule = true;
                mo["default"] = mo;
                module.exports = mo;
            } else {
                root.miniTpl = factory();
            }
        })(this, factory);
复制代码

先看一下这一段

入口

(function (root, factory) {
            if (typeof define === "function" && define.amd) {
                define(factory);
            } else if (typeof exports === "object") {
                var mo = factory();
                mo.__esModule = true;
                mo["default"] = mo;
                module.exports = mo;
            } else {
                root.miniTpl = factory();
            }
        })(this, factory);
复制代码

将上下文this与构造函数factory放入自执行函数,进行环境判断,这里我们进入第三个判断

root.miniTpl = factory()  //root即上下文,这里指window
复制代码

factory

factory最终会返回一个render函数,在render函数中先对data进行初始化赋值,list是用来存拼接最终的函数。接下来会执行transform函数,这个函数是用来格式化我们传入的innerHTML(传入的是一个字符串)

transform

     var reg = /<%([\s\S]*?)%>/g
复制代码

定义了一个正则匹配我们的标识符<%和%>用exec函数进行匹配,此函数会返回需要用到的2个参数

  1. 去掉<%和%>的内容
  2. 出现的下标位置

每个匹配到的会记录最新的分割的下标,以及起始下标(nowIndex),在appendTxt函数中,将不在<% %>中的字段也push到arr中去存储起来,最终会得到<% %>闭合标签数量*2+1个length的arr。 这个+1是因为最后一个%>外面还有一个闭合标签

appendTxt

会对传入的参数进行修改,因为传入参数的类型是引用类型,因此会同步修改

字符串函数拼接

得到上文返回的codeArr =arr

for (var i = 0, len = codeArr.length; i < len; i++) {
                    var item = codeArr[i];
                    if (item.type == 1) { //html标签
                        list.push(item.txt);
                    } else if (item.type == 2) { //需要赋值的语句
                        var txt = "tpl+=" + item.txt + ";";
                        list.push(txt);
                    } else {
                        var txt = 'tpl+="' + item.txt.replace(/"/g, '\\"') + '";';
                        list.push(txt);
                    }
                }
复制代码

type =1指的是普通的函数语句,例如for() else{}

type =2指的是赋值语句,例如 =

else则指的普通html标签

最后不上一句

"return tpl;"
复制代码

new Function

此刻拼接的字符串是

var tpl = "";
tpl+="\n        <ul>\n            ";
 for(var i=0; i < data.length; i++){
            var item = data[i];
            if(item.age < 30){
tpl+="\n                <li>我的名字是\n                    ";
tpl+=item.name;
tpl+=",我的年龄是\n                        ";
tpl+=item.age;
tpl+="\n                </li>\n                ";
}else{
tpl+="\n                    <li>my name is\n                        ";
tpl+=item.name;
tpl+=",my age is a sercet.</li>\n                    ";
}
tpl+="\n                        ";
 } 
tpl+="\n        </ul>\n    ";
return tpl;
复制代码

接下来进行到

return new Function("data", list.join("\n"))(data)
复制代码

这个作用和eval类似,先进行字符串操作,再执行函数操作,强就强在可以传入参数()()也表明是一个自执行函数,将结果往上返回

最后

将得到的string赋值给innerHTML 进行浏览器渲染,就得到想要的html。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值