【正则】网站前端开发模板插件原理

慕课网电商网站前端开发源码|网站前端开发与后台开发|前端开发博客网站

声明:本文内容属于重磅出击。没有参考任何文章。书籍倒有,后面会提到。
如遇到类似此文的文章,纯属巧合,或者是天意。。。

第一个问题:为什么需要模板?
因为我们从后台拿到的数据后,要动态拼接成html,再渲染到页面中。
但有个问题,不使用模板的话,html字符串的拼接操作都写在js中,维护起来将非常麻烦。
因为没有做到视图和数据分离。
项目中其实经常需要对同一批数据,有不同的展示。
也就是需要实现多个视图,那么模板就有天然的价值。

第二个问题:都有那些模板插件?
百度吧!我使用过underscore.template、jquery.tmpl.js
二者的源码我都读过。尤其前者在underscore源码分析时专门写一篇文章。

第三个问题:本文的目的是什么?
任何东西,只要能写出自己的,使用别人的就能更顺手了。
其实本文的最初目的,其实还是研究正则相关案例。
通过实现一个自己的模板函数,能体会正则的强大,同时也了解下模板相关知识点。
里面用到的所用知识点,我都会稍微讲解一下。
保证只要懂js的同学都有信心看完此文。
看完之后,把最后的代码敲一遍。
然后可以自豪得跟人说了:模板嘛,这东西有啥难的,看我分分钟,跟你写个。
此话一甩出去,顿时在对方眼中,就是大神的感觉!有木有。

正文开始。

先用underscore的模板函数来创建一个最简单但又不平凡的案例。
html 代码

<meta charset=”utf-8″>
<style>
    table,
    td {
        border: 1px solid black;
    }
</style>
<body></body>
<script src=”https://cdn.bootcss.com/underscore.js/1.8.3/underscore.js”></script>
<script type=”text/template” id=”tmpl”>
    <p>人员总数:
        <%= members.length %>
    </p>
    <table class=”container”>
        <% for (var i =0; i < members.length; i++) { %>
            <tr>
                <td>
                    <%= members[i].name %>
                </td>
                <td>
                    <% if (members[i].sex == 1) { %>
                        男
                        <% } else {%>
                            女
                            <% } %>
                </td>
                <td>
                    <%= members[i].age %>岁</td>
            </tr>
            <% } %>
    </table>
</script>
<script>
    window.onload = function () {
        var data = [{
            name: ‘laoyao’,
            sex: 1,
            age: 18
        }, {
            name: ‘xiaoyao’,
            sex: 0,
            age: 17
        }];
        var string = document.getElementById(‘tmpl’).innerHTML;
        document.querySelector(‘body’).innerHTML = _.template(string)({
            members: data
        });
    };
</script>

我们来看看是怎么用的。
1.把模板字符串放到script标签。
因为script标签也是dom节点,里面放些字符是没问题的。
因为script的type指定浏览器不认识的类型(比如text/template),
浏览器就会把script标签当做普通标签来处理,不会尝试去执行其内部代码。
2.用<%= …%>表示此处是值。
3.用<% …%>表示此处是js代码片段
4.方法_.template(string)返回的是一个函数。

上面就是模板使用的直观体验。
ok,我们就以这个例子入手,写出自己的模板方法。

知识点铺垫(照顾一下js初学者,可以略去不看):
1.用数组来拼接字符串
javascript 代码

var p = [‘I’, ‘love’, ‘you’].join(‘ ‘);
console.log(p);
// => I love you

是替换加号,一种优雅的方式
2.with用法
javascript 代码

var name = ‘111’;
var other = ‘xxx’;
var object = {
    name: ‘222’
};
with(object) {
    console.log(name); // => 222
    console.log(other); // => xxx
    name = ‘333’;
    other = ‘444’;
    haha = ‘555’;
}
console.log(name); // => 111
console.log(other); // => 444
console.log(haha); // =>555
console.log(object.name); // => 333
console.log(object.haha) // => undefined

省去前缀的一种方式(不要滥用)
读取时,优先在object里找。
设置时,object中有,就设置object的。
3.函数的构造函数式声明
javascript 代码

var sum = new Function(‘x’, ‘y’, ‘return x + y’);
var result = sum(1, 2);
console.log(result); // => 3

通过字符串来创建函数(不要滥用)
构造函数的参数依次为,函数所需参数已经函数体。
4.replace的用法
javascript 代码

var string = “1-2-3-4-a5”;
var result = string.replace(/-(\d)/g, ‘#$1’);
console.log(result);

把“-数字”替换成“#数字”,正则api中比较强大的工具。
其中$1指代正则括号中匹配的内容。

推理分析:

先看下,模板里的字符串:
<p>人员总数:<%= members.length %></p>
<table class=”container”>
<% for (var i =0; i < members.length; i++) { %>
<tr>
<td><%= members[i].name %></td>
<td><% if (members[i].sex == 1) { %>

<% } else {%>

<% } %>
</td>
<td><%= members[i].age %>岁</td>
</tr>
<% } %>
</table>
我们需要像如下的代码那样处理数据:
javascript 代码

function membersTemplate() {
    var p = [];
    p.push(‘ <p>人员总数:’);
    p.push(members.length);
    p.push(‘</p> <table class=”container”> ‘);
    for (var i = 0; i < members.length; i++) {
        p.push(‘<tr> <td>’);
        p.push(members[i].name);
        p.push(‘</td> <td>’);
        if (member[i].sex == 1) {
            p.push(‘男’);
        } else {
            p.push(‘女’);
        }
        p.push(‘</td> <td>’);
        p.push(members[i].age);
        p.push(‘岁</td> </tr>’);
    }
    p.push(‘</table>’);
    return p.join(”);
}

总体逻辑就是如上的代码,关键是,直接写members,浏览器是不认识的。
所以需要使用with:
javascript 代码

function membersTemplate(data) {
    var p = [];
    with(data) {
        p.push(‘ <p>人员总数:’);
        p.push(members.length);
        p.push(‘</p> <table class=”container”> ‘);
        for (var i = 0; i < members.length; i++) {
            p.push(‘<tr> <td>’);
            p.push(members[i].name);
            p.push(‘</td> <td>’);
            if (members[i].sex == 1) {
                p.push(‘男’);
            } else {
                p.push(‘女’);
            }
            p.push(‘</td> <td>’);
            p.push(members[i].age);
            p.push(‘岁</td> </tr>’);
        }
        p.push(‘</table>’);
    }
    return p.join(”);
}

上面的写法中,我把所有空白符,都换成了一个空格。不影响理解的。
ok,我们来测试一下:
html 代码

<meta charset=”utf-8″>
<style>
    table,
    td {
        border: 1px solid black;
    }
</style>
<body></body>
<script>
    function membersTemplate(data) {
        var p = [];
        with(data) {
            p.push(‘ <p>人员总数:’);
            p.push(members.length);
            p.push(‘</p> <table class=”container”> ‘);
            for (var i = 0; i < members.length; i++) {
                p.push(‘<tr> <td>’);
                p.push(members[i].name);
                p.push(‘</td> <td>’);
                if (members[i].sex == 1) {
                    p.push(‘男’);
                } else {
                    p.push(‘女’);
                }
                p.push(‘</td> <td>’);
                p.push(members[i].age);
                p.push(‘岁</td> </tr>’);
            }
            p.push(‘</table>’);
        }
        return p.join(”);
    }
</script>
<script>
    window.onload = function () {
        var data = [{
            name: ‘laoyao’,
            sex: 1,
            age: 18
        }, {
            name: ‘xiaoyao’,
            sex: 0,
            age: 17
        }];
        document.querySelector(‘body’).innerHTML = membersTemplate({
            members: data
        });
    };
</script>

上面的写法中,我们发现了一个问题:push方法经常连续调用。
众说周知,push方法是可以传进多个参数的。
因此我们改写如下:
javascript 代码

function membersTemplate(data) {
    var p = [];
    with(data) {
        p.push(‘ <p>人员总数:’, members.length, ‘</p> <table class=”container”>’);
        for (var i = 0; i < members.length; i++) {
            p.push(‘<tr> <td>’, members[i].name, ‘</td> <td>’);
            if (members[i].sex == 1) {
                p.push(‘男’);
            } else {
                p.push(‘女’);
            }
            p.push(‘</td> <td>’, members[i].age, ‘岁</td> </tr>’);
        }
        p.push(‘</table>’);
    }
    return p.join(”);
}

上面函数是我们的目标函数,下来要考虑的问题是,怎么生成这个函数。
我们分两步来(慢慢来嘛,省着扯到蛋)。
使用构造函数式来生成目标函数:
javascript 代码

function template(data) {
    // 函数体
    var body =
        “var p = [];” +
        “with(data) {” +
        “p.push(‘ 人员总数:’, members.length, ‘ <table class=\”container\”>’);” +
        “for (var i = 0; i < members.length; i++) {” +
        “p.push(‘<tr> <td>’, members[i].name, ‘</td> <td>’);” +
        “if (members[i].sex == 1) {” +
        “p.push(‘男’);” +
        “} else {” +
        “p.push(‘女’);” +
        “}” +
        “p.push(‘</td> <td>’, members[i].age, ‘岁</td> </tr>’);” +
        “}” +
        “p.push(‘</table>’);” +
        “}” +
        “return p.join(”);”
    var fn = new Function(‘data’, body);
    return fn(data);
}

测试结果如下:
html 代码

<meta charset=”utf-8″>
<style>
    table,
    td {
        border: 1px solid black;
    }
</style>
<body></body>
<script>
    function template(data) {
        // 函数体
        var body =
            “var p = [];” +
            “with(data) {” +
            “p.push(‘<p>人员总数:’, members.length, ‘ </p><table class=\”container\”>’);” +
            “for (var i = 0; i < members.length; i++) {” +
            “p.push(‘<tr> <td>’, members[i].name, ‘</td> <td>’);” +
            “if (members[i].sex == 1) {” +
            “p.push(‘男’);” +
            “} else {” +
            “p.push(‘女’);” +
            “}” +
            “p.push(‘</td> <td>’, members[i].age, ‘岁</td> </tr>’);” +
            “}” +
            “p.push(‘</table>’);” +
            “}” +
            “return p.join(”);”
        var fn = new Function(‘data’, body);
        return fn(data);
    }
</script>
<script>
    window.onload = function () {
        var data = [{
            name: ‘laoyao’,
            sex: 1,
            age: 18
        }, {
            name: ‘xiaoyao’,
            sex: 0,
            age: 17
        }];
        document.querySelector(‘body’).innerHTML = template({
            members: data
        });
    };
</script>

下来的问题,是最为关键的,
怎么由模板来生成目标函数的函数体?
我们仔细的对比下这两个字符串:
[quote]p.push(‘ <p>人员总数:’, members.length, ‘</p> <table class=”container”>’);
for (var i = 0; i < members.length; i++) {
p.push(‘<tr> <td>’, members[i].name, ‘</td> <td>’);
if (members[i].sex == 1) {
p.push(‘男’);
} else {
p.push(‘女’);
}
p.push(‘</td> <td>’, members[i].age, ‘岁</td> </tr>’);
}
p.push(‘</table>’);

<p>人员总数:<%= members.length %></p>
<table class=”container”>
<% for (var i =0; i < members.length; i++) { %>
<tr><td><%= members[i].name %></td>
<td><% if (members[i].sex == 1) { %>

<% } else {%>

<% } %>
</td>
<td><%= members[i].age %>岁</td>
</tr>
<% } %>
</table>[/quote]
不考虑空白符,
前者开头多了一个p.push(‘
前者结尾多了一个’);
中间的<%= %>和<% %>的变化方式有所不同,
比如<%= members[i].name %>变成了’,members[i].name,’
比如<% for (var i =0; i < members.length; i++) { %>
前面的<%变成了’);
后面的%>变成了p.push(‘

ok那么我们就用js的replace来实现这种变化吧:
javascript 代码

function template(string, data) {
    // 把空白符变成空格
    var string = string.replace(/\s+/g, ‘ ‘);
    // 替换<%= %>
    string = string.replace(/<%=(.*?)%>/g, “‘,$1,'”);
    // 替换<% %>的<%
    string = string.replace(/<%/g, “‘);”);
    // 替换<% %>的%>
    string = string.replace(/%>/g, “p.push(‘”)
    // 函数体
    var body =
        “var p=[];” +
        “with(data){” +
        “p.push(‘” +
        string +
        “‘);” +
        “}” +
        “return p.join(”);”
    var fn = new Function(‘data’, body);
    return fn(data);
}

测试情况如下:
html 代码

<meta charset=”utf-8″>
<style>
    table,
    td {
        border: 1px solid black;
    }
</style>
<body></body>
<script type=”text/template” id=”tmpl”>
    <p>人员总数:
        <%= members.length %>
    </p>
    <table class=”container”>
        <% for (var i =0; i < members.length; i++) { %>
            <tr>
                <td>
                    <%= members[i].name %>
                </td>
                <td>
                    <% if (members[i].sex == 1) { %>
                        男
                        <% } else {%>
                            女
                            <% } %>
                </td>
                <td>
                    <%= members[i].age %>岁</td>
            </tr>
            <% } %>
    </table>
</script>
<script>
    function template(string, data) {
        // 把空白符变成空格
        var string = string.replace(/\s+/g, ‘ ‘);
        // 替换<%= %>
        string = string.replace(/<%=(.*?)%>/g, “‘,$1,'”);
        // 替换<% %>的<%
        string = string.replace(/<%/g, “‘);”);
        // 替换<% %>的%>
        string = string.replace(/%>/g, “p.push(‘”)
        // 函数体
        var body =
            “var p=[];” +
            “with(data){” +
            “p.push(‘” +
            string +
            “‘);” +
            “}” +
            “return p.join(”);”
        var fn = new Function(‘data’, body);
        return fn(data);
    }
</script>
<script>
    window.onload = function () {
        var data = [{
            name: ‘laoyao’,
            sex: 1,
            age: 18
        }, {
            name: ‘xiaoyao’,
            sex: 0,
            age: 17
        }];
        var sting = document.querySelector(‘#tmpl’).innerHTML;
        document.querySelector(‘body’).innerHTML = template(sting, {
            members: data
        });
    };
</script>

至此模板的思想已经全部讲完。
一句话总结之,核心是正则,然后使用with和构造函数式函数声明。
但是上面的代码还有很多bug的。
我又不想这么收尾此文。最后贴一下《javascript忍者秘籍》里给的实现(我参考的是这本书):
javascript 代码

(function () {
    var cache = {};
    this.tmpl = function tmpl(str, data) {
        var fn = !/\W/.test(str) ?
            cache[str] = cache[str] || tmpl(document.getElementById(str).innerHTML) :
            new Function(‘obj’,
                “var p=[];” +
                ” var print=function(){p.push.apply(p,arguments)};” +
                “with(obj){p.push(‘” +
                str.replace(/[\r\t\n]/g, ‘ ‘)
                .split(“<%”).join(‘\t’)
                .replace(/((^|%>)[^\t]*)’/g, “$1\r”)
                .replace(/\t=(.*?)%>/g, “‘,$1,'”)
                .split(‘\t’).join(“‘);”)
                .split(“%>”).join(“p.push(‘”)
                .split(“\r”).join(“\\'”) +
                “‘);}return p.join(”);”);
        return data ? fn(data) : fn;
    }
})();

其测试用例如下(如果以后有心情再把它详细的分析一下)
html 代码

<script type=”text/template” id=”color1″>
    <p>here’s list of
        <%= items.length %> items:</p>
    <ul>
        <% for (var i =0; i < items.length; i++) { %>
            <li style=”color:<%= colors[i % colors.length]%>”>
                <%= items[i] %>
            </li>
            <% } %>
    </ul>
</script>
<script type=”text/template” id=”color2″>
    <p>here’s list of
        <%= items.length %> items:</p>
    <ul>
        <% for (var i =0; i < items.length; i++) {
print(‘<li style=”color:’, colors[i % colors.length], “\”>”, items[i], ‘</li>’);
} %>
    </ul>
</script>
<script>
    var colorsArray = [‘red’, ‘green’, ‘blue’, ‘orange’];
    var items = [];
    for (var i = 0; i < 10000; i++) {
        items.push(“test”);
    }
    function replaceContent(name) {
        document.getElementById(‘content’).innerHTML = tmpl(name, {
            colors: colorsArray,
            items: items
        })
    }
</script>
<script>
    (function () {
        var cache = {};
        this.tmpl = function tmpl(str, data) {
            var fn = !/\W/.test(str) ?
                cache[str] = cache[str] || tmpl(document.getElementById(str).innerHTML) :
                new Function(‘obj’,
                    “var p=[];” +
                    ” var print=function(){p.push.apply(p,arguments)};” +
                    “with(obj){p.push(‘” +
                    str.replace(/[\r\t\n]/g, ‘ ‘)
                    .split(“<%”).join(‘\t’)
                    .replace(/((^|%>)[^\t]*)’/g, “$1\r”)
                    .replace(/\t=(.*?)%>/g, “‘,$1,'”)
                    .split(‘\t’).join(“‘);”)
                    .split(“%>”).join(“p.push(‘”)
                    .split(“\r”).join(“\\'”) +
                    “‘);}return p.join(”);”);
            return data ? fn(data) : fn;
        }
    })();
</script>
<body>
    <input type=”button” value=”run colors1″ onclick=”replaceContent(‘color1’)”>
    <input type=”button” value=”run colors1″ onclick=”replaceContent(‘color2’)”>
    <p id=”content”>replace content will go here</p>
</body>

补充:如果不用模板的话,那应该是什么样的,针对此案例这里给出三个写法(注意格式化,便于维护)html 代码

<meta charset=”utf-8″>
<style>
    table,
    td {
        border: 1px solid black;
    }
</style>
<body>
    <div id=”div1″></div>
    <div id=”div2″></div>
    <div id=”div3″></div>
</body>
<script>
    var members = [{
        name: ‘laoyao’,
        sex: 1,
        age: 18
    }, {
        name: ‘xiaoyao’,
        sex: 0,
        age: 19
    }];
</script>
<script>
    // 写法一
    var html = [
        ‘<p>人员总数:’, members.length, ‘</p>’,
        ‘<table class=”container”>’,
        members.map(function (item) {
            return [
                “<tr>”,
                “<td>”, item.name, “</td>”,
                “<td>”, item.sex == 1 ? ‘男’ : ‘女’, ‘</td>’,
                “<td>”, item.age, “岁</td>”,
                “</tr>”
            ].join(”);
        }).join(”),
        ‘</table>’
    ].join(”);
    document.getElementById(‘div1’).innerHTML = html;
</script>
<script>
    // 写法二
    var html = [
        ‘<p>人员总数:’, members.length, ‘</p>’,
        ‘<table class=”container”>’,
        (function (data) {
            var rows = ”;
            for (var i = 0; i < data.length; i++) {
                var item = data[i];
                rows += [
                    “<tr>”,
                    “<td>”, item.name, “</td>”,
                    “<td>”, item.sex == 1 ? ‘男’ : ‘女’, ‘</td>’,
                    “<td>”, item.age, “岁</td>”,
                    “</tr>”
                ].join(”);
            }
            return rows;
        })(members),
        ‘</table>’
    ].join(”);
    document.getElementById(‘div2’).innerHTML = html;
</script>
<script>
    // 写法三
    var html = ”;
    html += ‘<p>人员总数:’ + members.length + ‘</p>’ +
        ‘<table class=”container”>’;
    for (var i = 0; i < members.length; i++) {
        var item = members[i];
        html +=
            “<tr>” +
            “<td>” + item.name + “</td>” +
            “<td>” + (item.sex == 1 ? ‘男’ : ‘女’) + ‘</td>’ +
            “<td>” + item.age + “岁</td>” +
            “</tr>”
    }
    html += ‘</table>’;
    console.log(html)
    document.getElementById(‘div3’).innerHTML = html;
</script>

前端开发功能文档|前端制定开发文档|广州4399游戏前端开发

» 本文来自:前端开发者 » 《【正则】网站前端开发模板插件原理》
» 本文链接地址:https://www.rokub.com/3934.html
» 您也可以订阅本站:https://www.rokub.com
赞(0)
64K

评论 抢沙发

评论前必须登录!