ejs学习笔记-源码(三)

EJS全局属性

EJS有三个比较重要的全局属性,分别为:

  • cache,设置模板是否缓存,默认值为true
    当你通过element或url创建EJS实例时,会在EJS.templates_directory对象中添加相应的属性(属性名为element的id或url),该属性为对象,其中text属性值为模板的内容。相应的有EJS.get(path, cache)EJS.update(path, template)的方法获取、更新缓存
  • type,设置模板的匹配符,默认为<
  • ext,通过url加载模板的文件后缀,默认为.ejs

EJS.Scanner类

我理解这个Scanner为一个解析器,用来解析EJS模板,它会解析模板非空的每行代码,然后对每行代码通过匹配符分隔后的字符串进行编译。匹配符为/(<%%)|(%%%%>)|(<%=)|(<%#)|(<%)|(%>\n)|(%>)|(\n)//(\[%%)|(%%\])|(\[%=)|(\[%#)|(\[%)|(%\]\n)|(%\])|(\n)/

原型方法scan

解析字符串,用分隔符\n将字符串分隔,保存到数组中,实现代码如下:

1
var source_split = rsplit(this.source, /\n/);
for(var i=0; i<source_split.length; i++) {
 var item = source_split[i];
 this.scanline(item, regex, block);
}
//如以下代码
<h1>date_tag</h1>
<div class="row"><%= date_tag('date', new Date())%></div>
<% var a = 'hello'; %>
//source_split数组值为
[0] '<h1>date_tag</h1>'
[1] '\n'
[2] '<div class="row"><%= date_tag('date', new Date())%></div>'
[3] '\n'
[4] '<% var a = 'hello'; %>'

原型方法scanline

解析字符串,通过分隔符将字符串分隔,然后执行回调函数。

1
var line_split = rsplit(line, regex);
for(var i=0; i < line_split.length; i++) {
    var token = line_split[i];
    if (token != null) {
        try{
            block(token, this);
        }catch(e){
            throw {type: 'EJS.Scanner', line: this.lines};
        }
    }
}
若line = <% var a = "hello"; %>
则line_split = ['<%', ' var a = "hello"'; '%>']

EJS.Scanner.to_text

如果参数是Date对象则返回toDateString(),若为其他对象返回toString(),否则返回空字符串。

EJS.Compiler类

这是EJS核心,该类对模板进行编译,其中实例属性scannerEJS.scanner实例对象。

原型方法compile

编译模板,生成字符串to_be_evaled,再通过eval(to_be_evaled)声明EJS.Compiler方法process

如模板:

1
<h1>headers</h1>
<h2><%= title%></h2>
<%# this is comment %>
<%
var choices = [ 
  {value: 1, text: 'First Choice' }, 
  {value: 2, text: 'Second Choice'},
  {value: 3, text: 'Third Choice'}
];
%>
<div><%= select_tag('selectElm', 2, choices)%></div>

process方法为:

1
this.process = function(_CONTEXT,_VIEW) {
    try { 
        with(_VIEW) { 
            with (_CONTEXT) {
                var ___ViewO = [];; ___ViewO.push("<h1>headers</h1>\n");
                ___ViewO.push("<h2>"); 
                ___ViewO.push((EJS.Scanner.to_text( title)));
                ___ViewO.push("</h2>\n");
                ___ViewO.push("\n");
                var choices = [ 
                  {value: 1, text: 'First Choice' }, 
                  {value: 2, text: 'Second Choice'},
                  {value: 3, text: 'Third Choice'}
                ];
                ___ViewO.push("\n");
                ___ViewO.push("<div>");
                ___ViewO.push((EJS.Scanner.to_text( select_tag('selectElm', 2, choices)))); 
                ___ViewO.push("</div>"); 
                return ___ViewO.join('');
            }
        }
    }
    catch(e){e.lineNumber=null;throw e;}
};

EJS类

EJS模板引擎类,初始化模板,并解析编译。

原型方法render

返回为return this.template.process.call(object, object,v),其中this.template.processEJS.Compiler实例对象编译模板后动态定义的process方法,其返回值为字符串,见上文“原型方法complie”。

参数vEJS.Helpers实例对象,使模板能使用视图工具。

原型方法update

用法参考ejs学习笔记(一),这里注意的是该方法最后调用的还是原型方法render,见element.innerHTML = this.render(options)

预编译ejs模板

EJS构造函数初始化实例时,有以下代码:

1
if(options.precompiled){
    this.template = {};
    this.template.process = options.precoimpiled;
    EJS.update(this.name, this);
    return;
}

如此你使用EJS模板引擎时,可预编译好模板,在页面中加载时就省去编译模板的时间,对繁杂且大的模板很有用。如你有编译好的模板template.js,该文件记得是加上myTemplateFunction =,函数名随便取。在页面就可以这样使用:

1
var url = 'template.js';
new EJS({url: url, precompiled: eval(EJS.request(url))});