##underscore模板插件定製 加入特性:html
一、嵌套模板ajax
二、支持渲染過濾器模式{{A | filterA:arg1..}}express
三、緩存(考慮加入localstorage?)api
四、調試緩存
todo性能優化
性能優化app
異步渲染異步
###代碼以下 <!-- lang: js --> (function (_) { _.templateSettings = { evaluate : /{%([\s\S]+?)%}/g, interpolate: /{{([\s\S]+?)}}/g, escape : /{%-([\s\S]+?)%}/g, include : /{%include([\s\S]+?)%}/g, isCache : true, isDebug : true, }; // When customizing templateSettings
, if you don't want to define an // interpolation, evaluation or escaping regex, we need one that is // guaranteed not to match. var noMatch = /(.)^/;async
// Certain characters need to be escaped so that they can be put into a // string literal. var escapes = { "'" : "'", '\\' : '\\', '\r' : 'r', '\n' : 'n', '\t' : 't', '\u2028': 'u2028', '\u2029': 'u2029' }; var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; /** * 參考template * add: * filter,能夠存在data、或者全局filters裏面 */ //gloabal filter var filters = _.filters = {}, //模板緩存類 tplCache = {}; _.addFilters = function (obj, fn) { if ( typeof obj == 'object' ) { _.extend(filters, obj); } else if ( typeof obj == 'string' ) { _.extend(filters, {obj: fn}); } } _.template2 = function (text, data, settings) { settings = _.defaults({}, settings, _.templateSettings); //模板外部引入include (function replaceInclude() { text = text.replace(settings.include, function (match, result) { var url = $.trim(result) var ret = syncGetTpl(url) return ret }) //模板中還有模板 if ( settings.include.test(text) ) { replaceInclude(); } })() // Combine delimiters into one regular expression via alternation. var matcher = new RegExp([ (settings.escape || noMatch).source, (settings.interpolate || noMatch).source, (settings.evaluate || noMatch).source ].join('|') + '|$', 'g'); // Compile the template source, escaping string literals appropriately. var index = 0; var source = "__p+='"; text.replace(matcher, function (match, escape, interpolate, evaluate, offset) { source += text.slice(index, offset) .replace(escaper, function (match) { return '\\' + escapes[match]; }); if ( escape ) { source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; } //解析屬性表達式、、支持過濾器 //example:value|fn2:a1,a2|fn3:b1,b2,b3 if ( interpolate ) { source += "'+\n((__t=(" + getAssembleFnStr() + "))==null?'':__t)+\n'"; } function getAssembleFnStr() { var filterChain = interpolate.split('|'), curFilterIdx = -1; var filterStack = []; return _(filterChain).reduce(function (val, filter) { //filter有兩種;一種function、另外一種functionName if ( filter.length ) { curFilterIdx++; var fname = filter.split(':'), name = fname[0], args = fname[1] || '', callFnstr = new Function('x', 'return x'); //name is function Str // var nameFn = new Function('return name')() var exp = ''; if ( name.indexOf('function') > -1 ) { exp = 'var filterFunc = name;\n' + 'return name.apply(_,_.flatten(['+val+', args.split(",")]))'; } //debug模式 else if ( name == 'debug' ) { var beforeFilter = filterChain[curFilterIdx], beforeFilterName = beforeFilter.split(':')[0], beforeFilterFn = beforeFilterName.indexOf('function') > -1 ? beforeFilterName : filters[beforeFilterName] exp = '\n(function(){\n' + 'var fv = '+val+';\n'+ (settings.isDebug ? ( "console.debug(\"Filter:" + beforeFilter + "\");\n" + "console.debug(\"%cOutput:\" +fv" + ",'color:red');\n" + (curFilterIdx > 0 ? "console.debug(\"FilterFn:" + beforeFilterName + ":\"," + beforeFilterFn + ");\n" : "") ) : ";" ) + '\nreturn fv;' + '\n})()\n'; } else { if ( filters[name] ) { exp = '(' + filters[name] + ')' + '(' + val + (args.length ? (',' + args) : '') + ')' } else { console.error('[' + name + ']filter:doesnt exist!') exp = val; } } return exp; } else { return val; } }) } if ( evaluate ) { source += "';\n" + evaluate + "\n__p+='"; } index = offset + match.length; return match; }); source += "';\n"; // If a variable is not specified, place data values in local scope. if ( !settings.variable ) source = 'with(obj||{}){\n' + source + '}\n'; source = "var __t,__p='',__j=Array.prototype.join," + "print=function(){__p+=__j.call(arguments,'');};\n" + source + "return __p;\n"; var render; try { render = new Function(settings.variable || 'obj', '_', source); } catch (e) { e.source = source; throw e; } if ( data && _.size(data) ) return render(data, _); var template = function (data) { return render.call(this, data, _); }; // Provide the compiled function source as a convenience for precompilation. template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; return template; }; _.tpl = function (htmlSelectorOrTplUrl, renderData, renderDomSeletor, renderedFn) { var htmlOriginal = $(htmlSelectorOrTplUrl).html(), t1 = _.now(), compiledFn = function (html) { var tpl = _.template2(html); var t2 = _.now() $(renderDomSeletor).html(tpl(renderData)); var t3 = _.now() debug('耗時分析', tpl.source, '\n[ms]compile time', t2 - t1, 'render time:', t3 - t2, 'total', t3 - t1) renderedFn ? renderedFn.apply(this) : null; }; if ( $.trim(htmlOriginal) == '' ) { //ajax請求獲取模板 $.get(htmlSelectorOrTplUrl, function (html) { compiledFn(html) }) } else { compiledFn(htmlOriginal) } } function debug(group) { if ( _.templateSettings.isDebug ) { console.group(group); _([].slice.call(arguments, 1)).each(function (text) { console.log(text); }) console.groupEnd(); } } function syncGetTpl(htmlSelectorOrTplUrl) { //若是一個url做爲selector進來會拋異常, //hack: var html = ''; //cache if ( _.templateSettings.isCache && tplCache[htmlSelectorOrTplUrl] ) { return tplCache[htmlSelectorOrTplUrl]; } try { if ( $(htmlSelectorOrTplUrl).length ) { html = $(htmlSelectorOrTplUrl).html(); } else { aj(); } } catch (e) { aj(); } function aj() { $.ajax({ url : htmlSelectorOrTplUrl + '?' + _.now(), type : 'get', async : false, success: function (resp) { html = resp } }) } if ( _.templateSettings.isCache ) { tplCache[htmlSelectorOrTplUrl] = html; } return html; } })(_)