一個簡單template engine

大佬的github

A simple template engine,only 50 rows.html

學習目的

  • 與大佬約飯有一種約會的感受(平淡臉.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>
複製代碼

vue

    中用特定的分割符注入代碼後,就能夠像寫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);
    複製代碼

    先看一下這一段git

    入口

    (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放入自執行函數,進行環境判斷,這裏咱們進入第三個判斷github

    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是由於最後一個%>外面還有一個閉合標籤bash

    appendTxt

    會對傳入的參數進行修改,由於傳入參數的類型是引用類型,所以會同步修改app

    字符串函數拼接

    獲得上文返回的codeArr =arrdom

    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。

    相關文章
    相關標籤/搜索