• JS模板引擎


    JS模板引擎 :ArtTemplate (2)

     

    上一篇初略的介绍了一下javascript中的模板引擎,有兴趣的可以戳 这里  。

    这一篇将带着大家一起做一个简易的模板引擎,

    上一篇介绍到:模板引擎其实做的就是两件事。

    1. 根据一定的规则,解析我们所定义的模板
    2. 根据数据以及模板生成html(其实背后也是用的字符串拼接)

    那么,首先,我们要有一个模板,一份数据,以及想生成的结果。

    例如:模板:

     View Code

    数据:

     View Code

    结果:

    现在,我们就来实现上面这个例子。

    首先,我们需要定义我们的template方法。

     View Code

    然后,获取我们缓存的template 的render方法

    复制代码
    template.get = function (templateName) {
            var cache;
            if (defaults.cache && cacheStore[templateName]) {
                cache = cacheStore[templateName];
            } else if (typeof document === 'object') {
                // 加载模板并编译
                var elem = document.getElementById(templateName);
                if (elem) {
                    var source = elem.innerHTML;
                    cache = compile(source);
                    if (templateName && defaults.cache) {
                        cacheStore[templateName] = cache;
                    }
                }
            }
            return cache;
     };
    复制代码

    上面的代码,相信大家也都看得懂,主要是对渲染方法进行缓存,以提升效率。接下来是其中最重要的编译方法。

    复制代码
        var compile = template.compile = function (source, options) {
            var options = extend(options, defaults);
            var render = compiler(source, options);
            return render;
        };
    
        var compiler = function (source, options) {
            var openTag = options.openTag;
            var closeTag = options.closeTag;
            var cache = options.cache;
    
            //此处开始解析模板,并生成渲染函数
            var headerCode = "'use strict';" + "
    " + "var ";
            var mainCode = "$out='';";
            var footerCode = "return new String($out);";
    
            var uniq = {};
    
            //对模板中html代码的处理
            var html = function (code) {
                if (code) {
                    code = "$out+=" + stringify(code) + ";" + "
    ";
                }
                return code;
            };
    
            //对模板中逻辑部分的处理
            var logic = function (code) {
                // 输出语句. 编码: <%=value%> 不编码:<%=#value%>
                if (code.indexOf('=') === 0) {
                    code = code.replace(/^=[=#]?|[s;]*$/g, '');
                    code = "$out+=" + code + ";";
                }
                // 提取模板中的变量名
                each(getVariable(code), function (name) {
                    // name 值可能为空,在安卓低版本浏览器下
                    if (!name || uniq[name]) {
                        return;
                    }
                    var value;
                    // 声明模板变量
                    value = "$data." + name;
                    headerCode += name + "=" + value + ",";
                    uniq[name] = true;
    
                });
                return code + "
    ";
            };
    
            each(source.split(closeTag), function (code) {
                //此时代码已被截取成两部分,一部分是纯html,
                //一部分是逻辑代码,即是包含在html<%logic%>html里面的部分
                code = code.split(openTag);
                var htmlStr = code[0];
    
                var logicStr = code[1];
    
                mainCode += html(htmlStr);
    
                if (code.length > 1 && logicStr) {
    
                    mainCode += logic(logicStr);
                }
            });
    
    
    
    
            var code = headerCode + mainCode + footerCode;
            try {
                var Render = new Function("$data", code);
                return Render;
    
            } catch (e) {
                e.temp = "function anonymous($data) {" + code + "}";
                throw e;
            }
    
    
        };
    复制代码

    这个方法,便是将模板引擎中的内容解析,并生成渲染方法。

    生成之后的渲染方法大概是下面这个样子。

    复制代码
    (function($data,$filename) {
        'use strict';
        var i=$data.i,list=$data.list,$out='';$out+='<ul>
    ';
        for (var i = 0; i < list.length; i ++) {
            $out+='
            <li>';
            $out+= list[i].text;
            $out+='</li>
    ';
        }
        $out+='
    </ul>';
        return new String($out);
    })
    复制代码

    模板引擎的解析细节:

    大家知道,想生成上面的方法,无非就是通过evel 或者Funciton 之类的方法,将一个字符串生成成一个function。

    这里采用的是  var Render = new Function("$data", code);来生成渲染方法。其中$data为参数,code为字符串。 Function方法的具体使用可以参考官方文档 点我

    那么,我们想要生成这个Render方法,最重要的事便是 拼接 code 了。

    拼接code时,headerCode 和 footerCode 是不变的,对任何模板都是一样的。所以重点工作在于中间的mainCode。

    这里,mainCode主要分为两部分,一部分是模板中的html部分,一部分是模板中的逻辑部分(也就是包含在<%><%>里面的)

    compiler通过分割<%>,将里面的逻辑部分和html部分分离出来,然后再分别处理。

    • html部分无需特殊处理,只需要拼接进mainCode就可以了。
    • logic部分,就需要特殊处理了。例如 例子中的title,这个值,我们需要判断是变量还是关键字(for if ..,这个稍后会讲如何提取),
      如果是变量,就必须先赋值:var title = $data.title,$data即为渲染方法的参数,也就是用户到时候会传进来的。
      然后再直接将title追加到 mainCode中去就可以了。

    至于logic部分,如何提取变量主要是通过正则表达式来提取。

    复制代码
    // 静态分析模板变量
        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 = //*[wW]*?*/|//[^
    ]*
    |//[^
    ]*$|"(?:[^"\]|\[wW])*"|'(?:[^'\]|\[wW])*'|s*.s*[$w.]+/g;
    
        var SPLIT_RE = /[^w$]+/g;  //非数字字母下滑线和$符以外的其他字符
    
        //生成这样的正则,用于匹配关键字   /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|arguments|let|yield|undefined/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);
        }
    复制代码

    这里,主要各个正则的用处都写了注释了。

    主要做了:

    1. 过滤掉系统关键字,
    2. 过滤掉数字开头的变量(不合法变量)
    3. 由于1,2部,此时可能首尾有逗号,故,去除首尾的逗号
    4. 根据一个或多个逗号,分隔成参数数组   如:param1,param2,,param3=> ["param1","param2","param3"]

    就这样,渲染方法就拼接完了。

    最后,这里只是实现了js模板中简易的功能。后期诸如helper,include,还会在继续讲。

    完整源码地址 : https://github.com/chen4342024/andyTemplate

    如果有哪一方面讲的有问题。望不吝指教~

    最后问个问题,电脑是win10系统,用什么博客编辑器比较好?官方推荐的window live writer 安装不了!!!

    一艘船如果没有目标,那无论海面上哪个方向的风,都是逆风! 个人主页: http://chenblog.sinaapp.com/
     
    分类: 前端
  • 相关阅读:
    《虚拟伙伴》AR增强现实应用开发总结
    捕获起英文名Edda的灵感来源,我的心愿是程序员这个行业能够男女人数平衡
    侯捷老师C++大系之C++面向对象开发:(一)不带指针的类:Complex复数类的实现过程
    【腾讯GAD暑期训练营游戏程序班】游戏中的物理系统作业说明文档
    socketAPI:一个最简单的服务器和对应的客户端C语言的实现
    C/C++实践笔记 008
    C/C++实践笔记 007
    学习java之泛型类和泛型方法
    学习java之利用泛型访问自己定义的类
    学习java之HashMap和TreeMap
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4917388.html
Copyright © 2020-2023  润新知