读mmTemplate by RubyLouvre

试着剖析下正美大大的模板
https://github.com/RubyLouvre/mmTemplate/blob/master/mmTemplate.js

  1. 起首是一trim函数,把字符串两头的空缺字符去掉
  2. 然后是依据id猎取模板字符串,用ejs.compile(source)编译成模版函数
  3. 末了用data来挪用模版函数

这3步骤中有一些细节处置惩罚

  • 用ejs.cache[id]缓存模版函数,防备反复编译
  • $(id, doc)[0] || doc.querySelectorAll(id)[0] || doc.getElementById(id.slice(1)) 降级运用dom选择器
  • 针对 script textarea标签内的文本举行unescape处置惩罚
  • opts对象用来设置
    opts.doc 模板文档对象
    opts.tid 模板缓存id:string
    opts.open tops.close 最先封闭标签 默许 浏览器端为:”<&&>” nodejs端为:””
    opts.[helpername] 在模板内部运用的自定义辅佐函数

因而重要就是ejs.compile函数的编译历程
起首关于其供应的模板例子

        <script type="tmpl" id="table_tmpl">
            <&= title() &>
            <table border=1 width="80%">
                <&- for(var i=0,tl = @trs.length,tr;i<tl;i++){  -&>
                    <&- tr = @trs[i]; -&>
                    <tr>
                        <td><&= tr.name;; &></td> <td><&= tr.age; &></td> <td><&= tr.sex || "男" &></td>
                    </tr>
                    <& } &>
            </table>
            <&# 怎么可能不支持图片 &>
            <img src="<&= @href &>">
        </script>

编译函数依据支解标记<&&> 将其支解,如今用|||为人人标记出来以下:

            |||= title() |||
            <table border=1 width="80%">
                |||- for(var i=0,tl = @trs.length,tr;i<tl;i++){  -|||
                    |||- tr = @trs[i]; -|||
                    <tr>
                        <td>|||= tr.name;; |||</td> <td>|||= tr.age; |||</td> <td>|||= tr.sex || "男" |||</td>
                    </tr>
                    ||| } |||
            </table>
            |||# 怎么可能不支持图片 |||
            <img src="|||= @href |||">

这些由|||支解的片断被分为 一般字符串js逻辑 两大类
支解要领是设置一个标志位 flag=true
flag === true 的时刻为一般字符串【默许值】
flag === false 的时刻为js逻辑
因为<&&>是最先、闭合逐一相对的,因而能够依据当前flag的值flag ? open : close去查找到下一个分隔符的开关范例和位置
一般js逻辑字符串分别被存放于 codesjs数组中

同时,根据字符的递次同步举行的是把一切的字符串编译成为一个匿名函数
先来一个时间字符串

var time = new Date * 1; // 1399372159719

匿名函数anonymous终究挪用情势为anonymous(codes, js, filters, helpers)
因而编译函数分别给 codes,js 映射了一个情势参数名 txt1399372159719, js1399372159719
如今匿名函数显现以下:

function anonymous(txt1399372159719, js1399372159719, filters, title ) {
  return function(data) {
    'use strict';
    try {
      var r = '',
      line1399372159719 = 0;

当碰到一般字符串的时刻,会转换成语句

r += txt1399372159719[index];

到碰到辅佐函数<&= title() &>时转换为:

r += title();

碰到<&- for(var i=0,tl = @trs.length,tr;i<tl;i++){ -&>为:

for (var i = 0, tl = data.trs.length, tr; i < tl; i++) {;

其他类推
终究函数【格式化后】为:

function anonymous(txt1399372159719, js1399372159719, filters, title /**/ ) {
  return function(data) {
    'use strict';
    try {
      var r = '',
      line1399372159719 = 0;;
      r += txt1399372159719[0];;
      line1399372159719 = 1;;
      r += title();;
      r += txt1399372159719[1];;
      line1399372159719 = 2;
      for (var i = 0, tl = data.trs.length, tr; i < tl; i++) {;
        r += txt1399372159719[2];;
        line1399372159719 = 3;
        tr = data.trs[i];;
        r += txt1399372159719[3];;
        line1399372159719 = 4;;
        r += tr.name;;;;
        r += txt1399372159719[4];;
        line1399372159719 = 5;;
        r += tr.age;;;
        r += txt1399372159719[5];;
        line1399372159719 = 6;;
        r += tr.sex || "男";;
        r += txt1399372159719[6];;
        line1399372159719 = 7;
      };
      r += txt1399372159719[7];;
      line1399372159719 = 8;;
      r += txt1399372159719[8];;
      line1399372159719 = 9;;
      r += data.href;;
      r += txt1399372159719[9];
      return r;
    } catch (e) {
      EJS.log(e);
      EJS.log(js1399372159719[line1399372159719 - 1])
    }
  }
}

注意到

  1. 天生的函数堪比手写但有一些冗余的分号;防备语句争执题目
  2. trim=true的体式格局处置惩罚标签冗余空缺,适用于不需要天生过剩空缺的状况
  3. @trs.length等语句会被转化为 data.trs.length,而不是简朴的猎取全局变量
  4. |过滤器会被网络起来,然后一个一个的实行递次嵌套实行
  5. #诠释语句将被完整疏忽
  6. helpers是被数组化,然后根据情势参数一次被匿名函数挪用的【参考title函数】
  7. 关于line1399372159719这个变量,我猜想是用于纪录编译效果函数的行数的,详细还不是很邃晓,愿望晓得的同砚能够帮助诠释一下

这部份的代码都位于js逻辑字符串分支的处置惩罚中
天生函数的体式格局为 Function.apply,这段代码能够浏览一下

        var body = ["txt"+time,"js"+time, "filters"]

        var fn = Function.apply(Function, body.concat(helperNames,t) );
        // console.log(fn.toString())
        var args = [codes, js, EJS.filters];
        var compiled = fn.apply(this, args.concat(helpers));

末了编译函数被(缓存)返回,供我们挪用 fn(data)

好厉害的代码,我还要打个饱嗝再逐步消化下。
特别是关于

line1399372159719 这个变量的作用 还在迷惑中,愿望有人能够帮助诠释下?

the End.

    原文作者:tcdona
    原文地址: https://segmentfault.com/a/1190000000498381
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞