Home  >  Article  >  Web Front-end  >  Detailed introduction to the application scenarios and implementation principles of JavaScript template engine

Detailed introduction to the application scenarios and implementation principles of JavaScript template engine

黄舟
黄舟Original
2017-03-16 14:58:281478browse

一、应用场景

以下应用场景可以使用模板引擎
1、如果你有动态ajax请求数据并需要封装成视图展现给用户,想要提高自己的工作效率。
2、如果你是拼串族或者数组push族,迫切的希望改变现有的书写方式。
3、如果你在页面布局中,存在共性模块和布局,你可以提取出公共模板,减少维护的数量。

二、实现原理

不同模板间实现原理大同小异,各有优缺,请按需选择,以下示例以artTemplate模板引擎来分析。

2.1 模板存放

模板一般都是放置到textarea/input等表单控件,或者script[type="text/html"]等标签中,如下:

<script id="test" type="text/html">
	{{if isAdmin}}

	<h1>{{title}}</h1>
	<ul>
	    {{each user as name i}}
	        <li> {{i + 1}} :{{name}}</li>
	    {{/each}}
	</ul>

	{{/if}}
</script>

//textarea或input则取value,其它情况取innerHTML

2.2 模板函数

一般都是templateFun(“id”, data);其中id为存放模板字符串的元素id,data为需要装载的数据。

2.3 模板获取

一般都是通过ID来获取,document.getElementById(“ID”):

//textarea或input则取value,其它情况取innerHTML
var html = /^(textarea|input)$/i.test(element.nodeName) ? element.value : element.innerHTML;

2.4 模板解析——处理html语句和逻辑语句及其他格式化处理

这步的主要操作其实多余的空格,解析出html元素和逻辑语句及关键字。例如:artTemplate.js中的代码实现:

defaults.parser = function (code, options) {
    // var match = code.match(/([\w\$]*)(\b.*)/);
    // var key = match[1];
    // var args = match[2];
    // var split = args.split(&#39; &#39;);
    // split.shift();

    //if isAdmin
    code = code.replace(/^\s/, &#39;&#39;);

    //["if", "isAdmin"]
    var split = code.split(&#39; &#39;);
    //if
    var key = split.shift();
    //isAdmin
    var args = split.join(&#39; &#39;);

    switch (key) {

        case &#39;if&#39;:
            //if(isAdmin){
            code = &#39;if(&#39; + args + &#39;){&#39;;
            break;

        case &#39;else&#39;:

            if (split.shift() === &#39;if&#39;) {
                split = &#39; if(&#39; + split.join(&#39; &#39;) + &#39;)&#39;;
            } else {
                split = &#39;&#39;;
            }

            code = &#39;}else&#39; + split + &#39;{&#39;;
            break;

        case &#39;/if&#39;:

            code = &#39;}&#39;;
            break;

        case &#39;each&#39;:

            var object = split[0] || &#39;$data&#39;;
            var as     = split[1] || &#39;as&#39;;
            var value  = split[2] || &#39;$value&#39;;
            var index  = split[3] || &#39;$index&#39;;

            var param   = value + &#39;,&#39; + index;

            if (as !== &#39;as&#39;) {
                object = &#39;[]&#39;;
            }

            code =  &#39;$each(&#39; + object + &#39;,function(&#39; + param + &#39;){&#39;;
            break;

        case &#39;/each&#39;:

            code = &#39;});&#39;;
            break;

        case &#39;echo&#39;:

            code = &#39;print(&#39; + args + &#39;);&#39;;
            break;

        case &#39;print&#39;:
        case &#39;include&#39;:

            code = key + &#39;(&#39; + split.join(&#39;,&#39;) + &#39;);&#39;;
            break;

例如上例中:”{{if isAdmin}}”最终被解析成”if(isAdmin){”,”{{/if}}“被解析成“}”。

2.5 模板编译——字符串拼接成生成函数的过程

这步的主要操作就是字符串的拼接成生成函数,看看artTemplate的部分源码:

function compiler (source, options) {
    /*
    openTag: &#39;<%&#39;,    // 逻辑语法开始标签
    closeTag: &#39;%>&#39;,   // 逻辑语法结束标签
    escape: true,     // 是否编码输出变量的 HTML 字符
    cache: true,      // 是否开启缓存(依赖 options 的 filename 字段)
    compress: false,  // 是否压缩输出
    parser: null      // 自定义语法格式器 @see: template-syntax.js
    */
    var debug = options.debug;
    var openTag = options.openTag;
    var closeTag = options.closeTag;
    var parser = options.parser;
    var compress = options.compress;
    var escape = options.escape;

    var line = 1;
    var uniq = {$data:1,$filename:1,$utils:1,$helpers:1,$out:1,$line:1};

    //isNewEngin在6-8返回undefined
    var isNewEngine = &#39;&#39;.trim;// &#39;proto&#39; in {}
    var replaces = isNewEngine
    ? ["$out=&#39;&#39;;", "$out+=", ";", "$out"]
    : ["$out=[];", "$out.push(", ");", "$out.join(&#39;&#39;)"];

    var concat = isNewEngine
        ? "$out+=text;return $out;"
        : "$out.push(text);";

    var print = "function(){"
    +      "var text=&#39;&#39;.concat.apply(&#39;&#39;,arguments);"
    +       concat
    +  "}";

    var include = "function(filename,data){"
    +      "data=data||$data;"
    +      "var text=$utils.$include(filename,data,$filename);"
    +       concat
    +   "}";

    var headerCode = "&#39;use strict&#39;;"
    + "var $utils=this,$helpers=$utils.$helpers,"
    + (debug ? "$line=0," : "");

    var mainCode = replaces[0];

    var footerCode = "return new String(" + replaces[3] + ");"

    // html与逻辑语法分离
    forEach(source.split(openTag), function (code) {
        code = code.split(closeTag);

        var $0 = code[0];
        var $1 = code[1];

        // code: [html]
        if (code.length === 1) {

            mainCode += html($0);

        // code: [logic, html]
        } else {

            mainCode += logic($0);

            if ($1) {
                mainCode += html($1);
            }
        }

    });

    var code = headerCode + mainCode + footerCode;

上例中模板中的模板字符串代码会被拼接成如下字符串:

&#39;use strict&#39;;
var $utils   = this,
	$helpers = $utils.$helpers,
	isAdmin  = $data.isAdmin,
	$escape  = $utils.$escape,
	title    = $data.title,
	$each    = $utils.$each,
	user     = $data.user,
	name     = $data.name,
	i        = $data.i,
	$out     = &#39;&#39;;

if (isAdmin) {
	$out += &#39;\n\n	<h1>&#39;;
	$out += $escape(title);
	$out += &#39;</h1>\n	<ul>\n	    &#39;;
	$each(user, function(name, i) {
		$out += &#39;\n	        <li>&#39;;
		$out += $escape(i + 1);
		$out += &#39; :&#39;;
		$out += $escape(name);
		$out += &#39;</li>\n	    &#39;;
	});
	$out += &#39;\n	</ul>\n\n	&#39;;
}
return new String($out);

然后会被生成如下函数:

var Render = new Function("$data", "$filename", code);

/*Outputs:
function anonymous($data, $filename) {
	&#39;use strict&#39;;
	var $utils   = this,
		$helpers = $utils.$helpers,
		isAdmin  = $data.isAdmin,
		$escape  = $utils.$escape,
		title    = $data.title,
		$each    = $utils.$each,
		user     = $data.user,
		name     = $data.name,
		i        = $data.i,
		$out     = &#39;&#39;;
	if (isAdmin) {
		$out += &#39;\n\n	<h1>&#39;;
		$out += $escape(title);
		$out += &#39;</h1>\n	<ul>\n	    &#39;;
		$each(user, function(name, i) {
			$out += &#39;\n	        <li>&#39;;
			$out += $escape(i + 1);
			$out += &#39; :&#39;;
			$out += $escape(name);
			$out += &#39;</li>\n	    &#39;;
		});
		$out += &#39;\n	</ul>\n\n	&#39;;
	}
	return new String($out);
}
 */
console.log(Render);

2.5 装载数据,视图呈现

/*Outputs:
<h1>User lists</h1>
<ul>
    <li>1 :zuojj</li>
    <li>2 :Benjamin</li>
    <li>3 :John</li>
    <li>4 :Rubby</li>
    <li>5 :Handy</li>
    <li>6 :CIMI</li> 
</ul>
*/
console.log(new Render(data, filename) + &#39;&#39;);
//对象转换为字符串
return new Render(data, filename) + &#39;&#39;;

三、常见JavaScript模板引擎及测试对比

  • BaiduTemplate —— 最简单好用的JS模板引擎(百度)

  • artTemplate —— 高性能JavaScript模板引擎(腾讯CDC)

  • Velocity.js —— 来自淘宝的JS 模板引擎

  • JavaScript Templates —— 轻量、快速、强大、无依赖模板引擎

  • Juicer —— 高效、轻量的Javascript模板引擎

  • mustache.js —— Logic-less {{mustache}} templates with JavaScript

  • 各大Javascript模板引擎测试对比

The above is the detailed content of Detailed introduction to the application scenarios and implementation principles of JavaScript template engine. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn