JavaScript模板引擎应用场景及实现原理详解


Posted in Javascript onDecember 14, 2018

本文实例讲述了JavaScript模板引擎应用场景及实现原理。分享给大家供大家参考,具体如下:

一、应用场景

以下应用场景可以使用模板引擎:

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模板引擎及测试对比

更多关于JavaScript相关内容可查看本站专题:《javascript面向对象入门教程》、《JavaScript查找算法技巧总结》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
用js实现计算代码行数的简单方法附代码
Aug 13 Javascript
js 第二代身份证号码的验证机制代码
May 12 Javascript
jQuery对JSON数据进行排序输出的方法
Jun 24 Javascript
jquery实现焦点轮播效果
Feb 23 Javascript
node.js中EJS 模板快速入门教程
May 08 Javascript
AngularJS使用拦截器实现的loading功能完整实例
May 17 Javascript
bootstrap表单示例代码分享
May 18 Javascript
jQuery实现html table行Tr的复制、删除、计算功能
Jul 10 jQuery
使用 vue.js 构建大型单页应用
Feb 10 Javascript
React中使用async validator进行表单验证的实例代码
Aug 17 Javascript
微信小程序常用简易小函数总结
Feb 01 Javascript
vue中配置scss全局变量的步骤
Dec 28 Vue.js
详解React 服务端渲染方案完美的解决方案
Dec 14 #Javascript
JS/HTML5游戏常用算法之路径搜索算法 A*寻路算法完整实例
Dec 14 #Javascript
JS实现的A*寻路算法详解
Dec 14 #Javascript
详解vue项目接入微信JSSDK的坑
Dec 14 #Javascript
微信小程序实现动态显示和隐藏某个控件功能示例
Dec 14 #Javascript
javascript中的event loop事件循环详解
Dec 14 #Javascript
如何在Vue中使用CleaveJS格式化你的输入内容
Dec 14 #Javascript
You might like
PHP使用DOMDocument类生成HTML实例(包含常见标签元素)
2014/06/25 PHP
PHP解码unicode编码的中文字符代码分享
2014/08/13 PHP
php生成微信红包数组的方法
2019/09/05 PHP
让元素在网页中可拖动示例代码
2013/08/13 Javascript
瀑布流布局代码一例
2014/04/11 Javascript
Javascript中innerHTML用法实例分析
2015/01/12 Javascript
javascript背景时钟实现方法
2015/06/18 Javascript
简单理解JavaScript中的封装与继承特性
2016/03/19 Javascript
Javascript 动态改变imput type属性
2016/11/01 Javascript
微信小程序 页面跳转及数据传递详解
2017/03/14 Javascript
vue构建单页面应用实战
2017/04/10 Javascript
深入理解 webpack 文件打包机制(小结)
2018/01/08 Javascript
js实时监控文本框输入字数的实例代码
2018/01/18 Javascript
node.js遍历目录的方法示例
2018/08/01 Javascript
从零开始搭建vue移动端项目到上线的步骤
2018/10/15 Javascript
微信小程序之几种常见的弹框提示信息实现详解
2019/07/11 Javascript
Vue form表单动态添加组件实战案例
2019/09/02 Javascript
JS实现点击下拉列表文本框中出现对应的网址,点击跳转按钮实现跳转
2019/11/25 Javascript
15分钟学会vue项目改造成SSR(小白教程)
2019/12/17 Javascript
Vue 解决路由过渡动画抖动问题(实例详解)
2020/01/05 Javascript
vue 如何使用递归组件
2020/10/23 Javascript
用Python脚本生成Android SALT扰码的方法
2013/09/18 Python
python实现文件快照加密保护的方法
2015/06/30 Python
浅谈python标准库--functools.partial
2019/03/13 Python
flask应用部署到服务器的方法
2019/07/12 Python
解决python 读取excel时 日期变成数字并加.0的问题
2019/10/08 Python
用python拟合等角螺线的实现示例
2019/12/27 Python
python标准库sys和OS的函数使用方法与实例详解
2020/02/12 Python
python ETL工具 pyetl
2020/06/07 Python
Python字典fromkeys()方法使用代码实例
2020/07/20 Python
综合内勤岗位职责
2014/04/14 职场文书
关于护士节的演讲稿
2014/05/26 职场文书
机关干部四风问题自查报告及整改措施
2014/10/26 职场文书
2015大学生实训报告
2014/11/05 职场文书
sql字段解析器的实现示例
2021/06/23 SQL Server
webpack介绍使用配置教程详解webpack介绍和使用
2022/06/25 Javascript