樂都引擎源碼,artTemplate模板引擎的源碼拜讀

 2023-10-07 阅读 19 评论 0

摘要:最初接觸的模板引擎還是基于node的ejs,當時覺得很神奇原來還可以這么玩,后來隨著學習的深入,使用過jade,doT等,當然還有一些比較火的諸如juicer、underscore還沒有深入接觸,直到今年上半年由于項目需要就想著要不試試騰訊的artTemp

最初接觸的模板引擎還是基于node的ejs,當時覺得很神奇原來還可以這么玩,后來隨著學習的深入,使用過jade,doT等,當然還有一些比較火的諸如juicer、underscore還沒有深入接觸,直到今年上半年由于項目需要就想著要不試試騰訊的artTemplate,感覺牛逼也吹的挺響的。開始了解后,覺得它比我之前使用過的jade、doT都好用,調試神馬的也方便很多,采用預編譯的方式也讓性能非常優越。

其實看了源碼后簡單的總結出來就是這么一句話:就是先獲取html中對應的id下得innerHTML,利用開始標簽和關閉標簽進行字符串切分,其實是將模板劃分成兩部份內容,一部分是html部分,一部分是邏輯部分,通過區別一些特殊符號比如each、if等來將字符串拼接成函數式的字符串,將兩部分各自經過處理后,再次拼接到一起,最后將拼接好的字符串采用new Function()的方式轉化成所需要的函數。

總共700多行的代碼,其實簡化下來就200多行代碼,

樂都引擎源碼?剛開始肯定是先定義方法:

?

 1 var template = function (filename, content) {
 2     return typeof content === 'string'
 3     ?   compile(content, {
 4             filename: filename
 5         })
 6     :   renderFile(filename, content);
 7 };
 8 
 9 
10 var renderFile = template.renderFile = function (filename, data) {
11     var fn = template.get(filename) || showDebugInfo({
12         filename: filename,
13         name: 'Render Error',
14         message: 'Template not found'
15     });
16     return data ? fn(data) : fn;
17 };

?

定義好后開始緩存fn方法,通過id獲取模板內容

template.get = function (filename) {var cache;if (cacheStore[filename]) {// 使用內存緩存cache = cacheStore[filename];} else if (typeof document === 'object') {// 加載模板并編譯var elem = document.getElementById(filename);if (elem) {var source = (elem.value || elem.innerHTML).replace(/^\s*|\s*$/g, '');cache = compile(source, {filename: filename});}}return cache;
};

源碼??

其實是對渲染的方法進行緩存,接下來開始編譯模板

var compile = template.compile = function (source, options) {// 合并默認配置options = options || {};for (var name in defaults) {if (options[name] === undefined) {options[name] = defaults[name];}}var filename = options.filename;try {var Render = compiler(source, options);} catch (e) {e.filename = filename || 'anonymous';e.name = 'Syntax Error';return showDebugInfo(e);}return render;};

?

把從模板中提取的代碼分成兩部分,一部分是html,一部分是邏輯部分,各自進行處理

function compiler (source, options) {var debug = options.debug;var openTag = options.openTag;var closeTag = options.closeTag;var parser = options.parser;var compress = options.compress;var escape = options.escape;var headerCode = "'use strict';"+ "var $utils=this,$helpers=$utils.$helpers,"+ (debug ? "$line=0," : "");var mainCode = replaces[0];var footerCode = "return new String(" + replaces[3] + ");"// html與邏輯語法分離forEach(source.split(openTag), function (code) {code = code.split(closeTag);var $0 = code[0];var $1 = code[1];// code: [html]if (code.length === 1) {mainCode += html($0);// code: [logic, html]} else {mainCode += logic($0);if ($1) {mainCode += html($1);}}});var code = headerCode + mainCode + footerCode;try {//將拼接好的字符串通過new Function的方法進行函數化var Render = new Function("$data", "$filename", code);Render.prototype = utils;return Render;} catch (e) {e.temp = "function anonymous($data,$filename) {" + code + "}";throw e;}// 處理 HTML 語句function html (code) {// 記錄行號line += code.split(/\n/).length - 1;// 壓縮多余空白與注釋if (compress) {code = code.replace(/\s+/g, ' ').replace(/<!--[\w\W]*?-->/g, '');}if (code) {code = replaces[1] + stringify(code) + replaces[2] + "\n";}return code;}// 處理邏輯語句function logic (code) {var thisLine = line;if (parser) {// 語法轉換插件鉤子code = parser(code, options);} else if (debug) {// 記錄行號code = code.replace(/\n/g, function () {line ++;return "$line=" + line +  ";";});}// 輸出語句. 編碼: <%=value%> 不編碼:<%=#value%>if (code.indexOf('=') === 0) {var escapeSyntax = escape && !/^=[=#]/.test(code);code = code.replace(/^=[=#]?|[\s;]*$/g, '');// 對內容編碼if (escapeSyntax) {var name = code.replace(/\s*\([^\)]+\)/, '');// 排除 utils.* | include | printif (!utils[name] && !/^(include|print)$/.test(name)) {code = "$escape(" + code + ")";}// 不編碼} else {code = "$string(" + code + ")";}code = replaces[1] + code + replaces[2];}// 提取模板中的變量名forEach(getVariable(code), function (name) {// name 值可能為空,在安卓低版本瀏覽器下if (!name || uniq[name]) {return;}var value;// 聲明模板變量// 賦值優先級:// [include, print] > utils > helpers > dataif (name === 'print') {value = print;} else if (name === 'include') {value = include;} else if (utils[name]) {value = "$utils." + name;} else if (helpers[name]) {value = "$helpers." + name;} else {value = "$data." + name;}headerCode += name + "=" + value + ",";uniq[name] = true;});return code + "\n";}};

template模板。?

在進行邏輯部分處理時,靜態分析模板變量;采用正則表達式首先過濾掉系統關鍵字,其次過濾掉不合法變量,再去除掉收尾的都好,最后根據都好分割成數組形式,以此來拼接字符串。再通過上面的new Function將字符串生成一個function,此為渲染方法,提供數據,進行剩下的結合。

var KEYWORDS =// 關鍵字'break,case,catch,continue,debugger,default,delete,do,else,false'+ ',finally,for,function,if,in,instanceof,new,null,return,switch,this'+ ',throw,true,try,typeof,var,void,while,with'// 保留字+ ',abstract,boolean,byte,char,class,const,double,enum,export,extends'+ ',final,float,goto,implements,import,int,interface,long,native'+ ',package,private,protected,public,short,static,super,synchronized'+ ',throws,transient,volatile'// ECMA 5 - use strict+ ',arguments,let,yield'+ ',undefined';var REMOVE_RE = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|\s*\.\s*[$\w\.]+/g;
var SPLIT_RE = /[^\w$]+/g;//非數字字母下滑線和$符以外的其他字符
var KEYWORDS_RE = new RegExp(["\\b" + KEYWORDS.replace(/,/g, '\\b|\\b') + "\\b"].join('|'), 'g');
var NUMBER_RE = /^\d[^,]*|,\d[^,]*/g;//匹配數字開頭或者逗號后緊跟著數字的
var BOUNDARY_RE = /^,+|,+$/g;//匹配開頭的一個或多個逗號以及結尾的 用于去除首尾的逗號
var SPLIT2_RE = /^$|,+/;//匹配多個逗號,用于分割 類似 param1,param2,,param3=> ["param1","param2","param3"] ,/^$/是為了匹配防止空字符串被切割// 獲取變量
function getVariable (code) {return code.replace(REMOVE_RE, '').replace(SPLIT_RE, ',').replace(KEYWORDS_RE, '').replace(NUMBER_RE, '').replace(BOUNDARY_RE, '').split(SPLIT2_RE);
};// 字符串轉義
function stringify (code) {return "'" + code// 單引號與反斜杠轉義.replace(/('|\\)/g, '\\$1')// 換行符轉義(windows + linux).replace(/\r/g, '\\r').replace(/\n/g, '\\n') + "'";
}

?

還有一些模板的輔助方法、錯誤事件、模板調試器等等

template.helper = function (name, helper) {helpers[name] = helper;
};var helpers = template.helpers = utils.$helpers;/*** 模板錯誤事件(可由外部重寫此方法)* @name    template.onerror* @event*/
template.onerror = function (e) {var message = 'Template Error\n\n';for (var name in e) {message += '<' + name + '>\n' + e[name] + '\n\n';}if (typeof console === 'object') {console.error(message);}
};// 模板調試器
var showDebugInfo = function (e) {template.onerror(e);return function () {return '{Template Error}';};
};

arttemplate中文文檔、?

最后return template;

// RequireJS && SeaJS
if (typeof define === 'function') {define(function() {return template;});// NodeJS
} else if (typeof exports !== 'undefined') {module.exports = template;
} else {this.template = template;
}

?

從網上找了一個artTemplate的結構圖,剛開始看可能看不明白,等看完源碼再看這張圖,會感覺清晰很多。image

art template、?

轉載于:https://www.cnblogs.com/yinsu12311/p/5816259.html

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/3/125917.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息