视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
详细介绍JavaScript模板引擎的应用场景及实现原理
2020-11-27 20:23:55 责编:小采
文档

一、应用场景

以下应用场景可以使用模板引擎:
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(' ');
 // split.shift();

 //if isAdmin
 code = code.replace(/^\s/, '');

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

 switch (key) {

 case 'if':
 //if(isAdmin){
 code = 'if(' + args + '){';
 break;

 case 'else':

 if (split.shift() === 'if') {
 split = ' if(' + split.join(' ') + ')';
 } else {
 split = '';
 }

 code = '}else' + split + '{';
 break;

 case '/if':

 code = '}';
 break;

 case 'each':

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

 var param = value + ',' + index;

 if (as !== 'as') {
 object = '[]';
 }

 code = '$each(' + object + ',function(' + param + '){';
 break;

 case '/each':

 code = '});';
 break;

 case 'echo':

 code = 'print(' + args + ');';
 break;

 case 'print':
 case 'include':

 code = key + '(' + split.join(',') + ');';
 break;

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

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

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

function compiler (source, options) {
 /*
 openTag: '<%', // 逻辑语法开始标签
 closeTag: '%>', // 逻辑语法结束标签
 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 = ''.trim;// 'proto' in {} var replaces = isNewEngine ? ["$out='';", "$out+=", ";", "$out"] : ["$out=[];", "$out.push(", ");", "$out.join('')"]; var concat = isNewEngine ? "$out+=text;return $out;" : "$out.push(text);"; var print = "function(){" + "var text=''.concat.apply('',arguments);" + concat + "}"; var include = "function(filename,data){" + "data=data||$data;" + "var text=$utils.$include(filename,data,$filename);" + concat + "}"; var headerCode = "'use strict';" + "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;

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

'use strict';
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 = '';

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

然后会被生成如下函数:

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

/*Outputs:
function anonymous($data, $filename) {
	'use strict';
	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 = '';
	if (isAdmin) {
	$out += '\n\n	<h1>';
	$out += $escape(title);
	$out += '</h1>\n	<ul>\n	 ';
	$each(user, function(name, i) {
	$out += '\n	 <li>';
	$out += $escape(i + 1);
	$out += ' :';
	$out += $escape(name);
	$out += '</li>\n	 ';
	});
	$out += '\n	</ul>\n\n	';
	}
	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) + '');
//对象转换为字符串
return new Render(data, filename) + '';

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

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

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

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

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

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

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

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

  • 下载本文
    显示全文
    专题