资讯专栏INFORMATION COLUMN

读mmTemplate by RubyLouvre

Sleepy / 3110人阅读

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

首先是一trim函数,把字符串两端的空白字符去掉

然后是根据id获取模板字符串,用ejs.compile(source)编译成模版函数

最后用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函数的编译过程
首先对于其提供的模板例子

        

编译函数根据分割符号<&&> 将其分割,现在用|||为大家标记出来如下:

            |||= title() |||
            
                |||- for(var i=0,tl = @trs.length,tr;i
                        
                    ||| } |||
            
|||= tr.name;; ||| |||= tr.age; ||| |||= tr.sex || "男" |||
|||# 怎么可能不支持图片 |||

这些由|||分割的片段被分为 普通字符串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为:

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])
    }
  }
}

注意到

生成的函数堪比手写但有一些冗余的分号;防止语句冲突问题

trim=true的方式处理标签冗余空白,适用于不需要生成多余空白的情况

@trs.length等语句会被转化为 data.trs.length,而不是简单的获取全局变量

|过滤器会被收集起来,然后一个一个的执行顺序嵌套执行

#注释语句将被完全忽略

helpers是被数组化,然后按照形式参数一次被匿名函数调用的【参考title函数】

对于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.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78088.html

相关文章

  • React16.2的fiber架构(2)

    摘要:为了帮助理解,我们继续加日志司徒正美,加群一起研究与只要收到更新对象,就会被调度程序调用。渲染器在将来的某个时刻调用。导步肯定为欢迎加继续略也是怒长,代码的特点是许多巨型类,巨型方法,有之遗风。 insertUpdateIntoFiber 会根据fiber的状态创建一个或两个列队对象,对象是长成这样的 //by 司徒正美, 加群:370262116 一起研究React与anujs //...

    Caicloud 评论0 收藏0
  • React16.2的fiber架构

    摘要:司徒正美,加群一起研究与用于调整渲染顺序,高优先级的组件先执行这只是一部分更新逻辑,简直没完没了,下次继续添上流程图,回忆一下本文学到的东西 React16真是一天一改,如果现在不看,以后也很难看懂了。 在React16中,虽然也是通过JSX编译得到一个虚拟DOM对象,但对这些虚拟DOM对象的再加工则是经过翻天覆地的变化。我们需要追根溯底,看它是怎么一步步转换过来的。我们先不看什么组件...

    lansheng228 评论0 收藏0
  • 《机票航班列表》项目总结

    摘要:很快没在公司参考这么大的项目升级工作了。第二个是事件,这两个事件无法冒泡,而的事件系统是建立在事件代理的基石之上。最大的收获是,终于有高度可用的版本了,也有一个可以为自己代言的大项目,我们的调试技术又大大提高了二分法总是最有效的。 很快没在公司参考这么大的项目升级工作了。工作的内容听起来很简单,将React改成我们平台事业部的迷你React框架Qreact(https://github...

    davidac 评论0 收藏0
  • Luy 1.0 :一个React-like轮子的诞生

    摘要:司徒正美的一款了不起的化方案,支持到。行代码内实现一个胡子大哈实现的作品其实就是的了源码学习个人文章源码学习个人文章源码学习个人文章源码学习个人文章这几片文章的作者都是司徒正美,全面的解析和官方的对比。 前言 在过去的一个多月中,为了能够更深入的学习,使用React,了解React内部算法,数据结构,我自己,从零开始写了一个玩具框架。 截止今日,终于可以发布第一个版本,因为就在昨天,我...

    codecook 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<