zoukankan      html  css  js  c++  java
  • 【原创】javascript模板引擎的简单实现

    本来想把之前对artTemplate源码解析的注释放上来分享下,不过隔了一年,找不到了,只好把当时分析模板引擎原理后,自己尝试

    写下的模板引擎与大家分享下,留个纪念,记得当时还对比了好几个模板引擎来着。

    这里所说的js的模板引擎,用的是原生的javascript语法,所以很类似php的原生模板引擎。

    前端模板引擎的作用?

    1. 可以让前端开发更简单,不需要为了生成一个dom结构而使用+运算符去拼接字符串,而只需要一个元素的(里面的html模板),或者一个变量(存储着模板),或者  一个模板文件

    2. 易于维护,减少耦合,假使你的dom结构变化了,不需要更改逻辑代码,而只需要更改对应的模板(文件)

    3. 可以缓存,如果你的模板是一个类似.tpl的文件,那么完全可以用浏览器去加载,并且还存下来。说到.tpl文件,可以做的不仅仅是缓存了,你还可以做到通过模块加载器

        将.tpl作为一个模块,那就可以按需加载文件,不是更省宽带,加快页面速度吗?

    4. 等等等

    前端模板引擎的原理?

    原理很简单就是 对象(数据)+ 模板(含有变量) -> 字符串(html)

    前端模板引擎的如何实现?

    通过解析模板,根据词法,将模板转换成一个函数,然后通过调用该函数,并传递对象(数据),输出字符串(html)

    (当然,具体的还要看代码的)

    就像这样:

    var tpl = 'i am <%= name%>, <%= age=> years old'; // <%=xxx>% 词法,标记为变量
    
    var obj = {
        name : 'lovesueee' ,
        age : 24
    };
    var fn = Engine.compile(tpl); // 编译成函数
    
    var str = fn(obj);   // 渲染出字符串        
    

    例子:

     demo

    简单的实现:

    (function (win) {
    
        // 模板引擎路由函数
        var ice = function (id, content) {
            return ice[
                typeof content === 'object' ? 'render' : 'compile'
            ].apply(ice, arguments);
        };
    
    
        ice.version = '1.0.0';
    
        // 模板配置
        var iConfig = {
            openTag  : '<%',
            closeTag : '%>'
        };
    
    
        var isNewEngine = !!String.prototype.trim;
    
        // 模板缓存
        var iCache = ice.cache = {};
    
        // 辅助函数
        var iHelper = {
            include : function (id, data) {
                return iRender(id, data);
            },
            print : function (str) {
                return str;
            }
        };
    
        // 原型继承
        var iExtend = Object.create || function (object) {
            function Fn () {};
            Fn.prototype = object;
            return new Fn;
        };
    
        // 模板编译
        var iCompile = ice.compile = function (id, tpl, options) {
    
            var cache = null;
    
            id && (cache = iCache[id]);
    
            if (cache) {
                return cache;
            }
    
            // [id | tpl]
            if (typeof tpl !== 'string') {
    
                var elem = document.getElementById(id);
    
                options = tpl;
    
                if (elem) {
                    // [id, options]
                    options = tpl;
                    tpl = elem.value || elem.innerHTML;
    
                } else {
                    //[tpl, options]
                    tpl = id;
                    id = null;
                }
            }
    
            options = options || {};
            var render  = iParse(tpl, options);
    
            id && (iCache[id] = render);
    
            return render;
        };
    
    
        // 模板渲染
        var iRender = ice.render = function (id, data, options) {
    
            return iCompile(id, options)(data);
        };
    
    
        var iForEach = Array.prototype.forEach ?
            function(arr, fn) {
                arr.forEach(fn)
            } :
            function(arr, fn) {
                for (var i = 0; i < arr.length; i++) {
                    fn(arr[i], i, arr)
                }
            };
    
    
        // 模板解析
        var iParse = function (tpl, options) {
    
            var html = [];
    
            var js = [];
    
            var openTag = options.openTag || iConfig['openTag'];
    
            var closeTag = options.closeTag || iConfig['closeTag'];
    
            // 根据浏览器采取不同的拼接字符串策略
            var replaces = isNewEngine
                ?["var out='',line=1;", "out+=", ";", "out+=html[", "];", "this.result=out;"]
                : ["var out=[],line=1;",  "out.push(", ");", "out.push(html[", "]);", "this.result=out.join('');"];
    
            // 函数体
            var body = replaces[0];
    
            iForEach(tpl.split(openTag), function(val, i) {
    
                if (!val) {
                    return;
                }
    
                var parts = val.split(closeTag);
    
                var head = parts[0];
    
                var foot = parts[1];
    
                var len = parts.length;
                // html
                if (len === 1) {
                    body += replaces[3] + html.length + replaces[4];
                    html.push(head);
    
                } else {
    
                    if (head ) {
                        // code
                        // 去除空格
                        head = head
                            .replace(/^s+|s+$/g, '')
                            .replace(/[
    
    ]+s*/, '')
    
    
                        // 输出语句
                        if (head.indexOf('=') === 0) {
                            head = head.substring(1).replace(/^[s]+|[s;]+$/g, '');
                
                            body += replaces[1] + head + replaces[2];
                        } else {
                            body += head;
                        }
    
                        body += 'line+=1;';
                        js.push(head);
                    }
                    // html
                    if (foot) {
                        _foot = foot.replace(/^[
    
    ]+s*/g, '');
                        if (!_foot) {
                            return;
                        }
                        body += replaces[3] + html.length + replaces[4];
                        html.push(foot);
                    }
                }
            });
    
            body = "var Render=function(data){ice.mix(this, data);try{"
                + body
                + replaces[5]
                + "}catch(e){ice.log('rend error : ', line, 'line');ice.log('invalid statement : ', js[line-1]);throw e;}};"
                + "var proto=Render.prototype=iExtend(iHelper);"
                + "ice.mix(proto, options);"
                + "return function(data){return new Render(data).result;};";
    
            var render = new Function('html', 'js', 'iExtend', 'iHelper', 'options', body);
    
            return render(html, js, iExtend, iHelper, options);
        };
    
        ice.log = function () {
            if (typeof console === 'undefined') {
                return;
            }
    
            var args = Array.prototype.slice.call(arguments);
    
            console.log.apply && console.log.apply(console, args);
    
        };
    
        // 合并对象
        ice.mix = function (target, source) {
            for (var key in source) {
                if (source.hasOwnProperty(key)) {
                    target[key] = source[key];
                }
            }
        };
    
        // 注册函数
        ice.on = function (name, fn) {
            iHelper[name] = fn;
        };
    
        // 清除缓存
        ice.clearCache = function () {
            iCache = {};
        };
    
        // 更改配置
        ice.set = function (name, value) {
            iConfig[name] = value;
        };
    
        // 暴露接口
        if (typeof module !== 'undefined' && module.exports) {
            module.exports = template;
        } else {
            win.ice = ice;
        }
    
    })(window);
  • 相关阅读:
    验证码识别思路
    chrome插件 图片搜索(20130602更新版本v2.0)
    CoffeeScript实现BF解释器
    浏览器几种高度宽度
    javascript Event事件
    浮躁的心
    前端页面跨域访问
    firefox浏览器兼容event事件方法
    转:Java Annotation详解
    javascript void(0)问题【转】
  • 原文地址:https://www.cnblogs.com/lovesueee/p/3513505.html
Copyright © 2011-2022 走看看