underscore.js 模板扩展

underscore模板插件定制

加入特性:

1、嵌套模板

2、支持渲染过滤器模式{{A | filterA:arg1..}}

3、缓存(考虑加入localstorage?)

4、调试

todo

性能优化

异步渲染


代码如下

<!-- 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 = /(.)^/;

    // 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;
    }
})(_)
时间: 2024-08-10 15:09:28

underscore.js 模板扩展的相关文章

Underscore.js 的模板功能介绍与应用

Underscore是一个非常实用的JavaScript库,提供许多编程时需要的功能的支持,他在不扩展任何JavaScript的原生对象的情况下提供很多实用的功能,需要了解的朋友可以详细参考下 Underscore是一个非常实用的JavaScript库,提供许多编程时需要的功能的支持,他在不扩展任何JavaScript的原生对象的情况下提供很多实用的功能. 无论你写一段小的js代码,还是写一个大型的HTML5应用,underscore都能帮上忙.目前,underscore已经被广泛使用,例如,b

HiShop2.x版本中的上传插件分析,得出所用的模板语言为Underscore.js 1.6.0且自己已修改

效果: 上传组件非常的酷,但是分析其使用JS写法使用了模板语言的,代码如下: <script type="text/j-template" id="tpl_popbox_ImgPicker_listItem"> <# _.each(dataset,function(url){ #> <li> <span class="img-list-overlay"><i class="img-l

underscore.js定义模板遇到问题:Uncaught TypeError: Cannot read property &#39;replace&#39; of undefined

代码正确缩进位置如下, extend "layout" block 'content',-> div ->'nihao' script id:"InvoiceItem",type:"text/template",style:"display: none",-> div ->"{{price}}" div ->"{{quantity}}" div ->&

underscore.js中模板函数应用

一.使用技术要点 (1)使用zepto.js的ajax请求; (2)使用underscore.js的_.template设定模板,模板一般以<script type="text/template"></script>,不是<template></template> (3)参数是以{data:listObj},而不是listObj (4)注意each里面data与item的对映 <!DOCTYPE html> <html&

模板引擎原理及underscore.js使用

为什么要使用模板引擎 DOM结构简单,完全可以使用DOM方法创建DOM树.$("<td></td").appendTo(); 当页面比较复杂的时候,下面的程序中红色部分来自JSON中的数据: <div class="feeds-item hasImg" id="item-702635"> <p class="feeds-item-pic"> <a href="http:

Underscore.js入门

1. Underscore对象封装 Underscore并没有在原生的JavaScript对象原型中进行扩展,而是像jQuery一样,将数据封装在一个自定义对象中(下文中称"Underscore对象"). 你可以通过调用一个Underscore对象的value()方法来获取原生的JavaScript数据,例如:  window.onload =   function () {          // 定义一个JavaScript内置对象            var jsData = 

Underscore.js(JavaScript对象操作方法)

Underscore封装了常用的JavaScript对象操作方法,用于提高开发效率.(Underscore还可以被使用在Node.js运行环境.) 在学习Underscore之前,你应该先保存它的API地址,因为你将在以后经常访问它: http://documentcloud.github.com/underscore/ 从API中,你已经可以看出,Underscore没有任何复杂的结构和流程,它仅仅提供了一系列常用的函数.如果你将API中的方法从头至尾用一遍,你就会对它非常了解. 尽管如此,但

Underscore.js 入门[转]

Underscore封装了常用的JavaScript对象操作方法,用于提高开发效率.它本身与我们介绍的主题“Backbone” 没有半毛钱的关系,因此你可以完全不理会“Backbone”的概念来学习它,或将它单独运用到任何一个页面.(另外,Underscore还可以被使用 在Node.js运行环境.) 在学习Underscore之前,你应该先保存它的API地址,因为你将在以后经常访问它: Underscore.js 1.7.0 中文文档(http://www.css88.com/doc/unde

Underscore.js 1.3.3 源码分析收藏

Underscore是一个提供许多函数编程功能的库,里面包含了你期待(在Prototype.js和Ruby中)的许多功能.但是没有扩展任何内置的Javascript对象,也就是说它没有扩展任何内置对象的原型.它被定位为jQuery和Backbone.js的基础层 源码注释转之网上他人之备注,特收藏以后方便阅读. // Underscore.js 1.3.3 // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. // Underscore is