文档章节

underscore.js 模板扩展

s
 snecker
发布于 2015/03/04 16:31
字数 734
阅读 180
收藏 2

##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;
    }
})(_)

© 著作权归作者所有

s
粉丝 1
博文 43
码字总数 10092
作品 0
长沙
技术主管
私信 提问
underscore.js 中的 template 用法

最近看了下 underscore.js ,看到里面有个 template 的方法,小使了一下,感觉挺不错的,觉得能满足我日常工作的需求了,而且使用起来也简单容易,具体用法可以 参考这里 。还有, undersco...

楠木楠
2016/11/03
658
0
web前端相关拓展库,框架集合

外观样式,响应式设计 1、Bootstrap 基于Bootstrap的衍生框架: BootMetro BootFlat http://bootflat.github.io/index.html Bootstrap Metro Dashboard 这是一款基于Bootstrap的Metro风格的后......

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

效果: 上传组件非常的酷,但是分析其使用JS写法使用了模板语言的,代码如下:   

easonjim
2016/12/27
0
0
Javascript扩展内建类型的利弊

不知道大家熟不熟悉Underscore.js这个库,就是个实用工具集,提供各种便捷方法啥的。 我以前自己也写过此类工具集,不过不同的是:underscore并没有扩展内建类型,而我写的那个是扩展自建类型...

Jeky
2012/11/13
589
2
Underscore.js 1.8.1 发布,JavaScript 工具库

Underscore.js 是一个实用的JavaScript工具库,提供了类似 Prototype 功能的编程支持,但没有对 JavaScript 内置的对象进行扩展。 此版本现已提供下载(ZIP),详细内容请看这里。...

oschina
2015/02/21
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

Oracle SQL语法实例合集

如需转载请注明出处https://my.oschina.net/feistel/blog/3052024 目的:迅速激活Oracle SQL 参考:《Oracle从入门到精通》 ------------------------------------------------------------......

LoSingSang
今天
1
0
增加 PostgreSQL 服务进程的最大打开文件数

https://serverfault.com/questions/628610/increasing-nproc-for-processes-launched-by-systemd-on-centos-7 要在systemd的配置里加才行...

helloclia
今天
2
0
组合模式在商品分类列表中的应用

在所有的树形结构中最适合的设计模式就是组合模式,我们看看常用商品分类中如何使用。 先定义一个树形结构的商品接口 public interface TreeProduct { List<TreeProduct> allProducts(...

算法之名
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部