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 相关文章推荐
jQuery 学习 几种常用方法
Jun 11 Javascript
jMessageBox 基于jQuery的窗口插件
Dec 09 Javascript
jquery捕捉回车键及获取checkbox值与异步请求的方法
Dec 24 Javascript
jQuery控制文本框只能输入数字和字母及使用方法
May 26 Javascript
分享JS数组求和与求最大值的方法
Aug 11 Javascript
深入理解jQuery3.0的domManip函数
Sep 01 Javascript
详解Vue2.X的路由管理记录之 钩子函数(切割流水线)
May 02 Javascript
Angular2使用Augury来调试Angular2程序
May 21 Javascript
vue router 配置路由的方法
Jul 26 Javascript
记录一篇关于redux-saga的基本使用过程
Aug 18 Javascript
JS中getElementsByClassName与classList兼容性问题解决方案分析
Aug 07 Javascript
Java Varargs 可变参数用法详解
Jan 28 Javascript
详解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在Web开发领域的优势
2006/10/09 PHP
Windows下PHP的任意文件执行漏洞
2006/10/09 PHP
浅析ThinkPHP中execute和query方法的区别
2014/06/13 PHP
codeigniter上传图片不能正确识别图片类型问题解决方法
2014/07/25 PHP
PHP实现的增强性mhash函数
2015/05/27 PHP
Add a Formatted Table to a Word Document
2007/06/15 Javascript
javascript写的一个链表实现代码
2009/10/25 Javascript
js类型检查实现代码
2010/10/29 Javascript
in.js 一个轻量级的JavaScript颗粒化模块加载和依赖关系管理解决方案
2011/07/26 Javascript
jQuery EasyUI API 中文文档 - Calendar日历使用
2011/10/19 Javascript
三种方式获取XMLHttpRequest对象
2014/04/21 Javascript
原生javascript实现的分页插件pagenav
2014/08/28 Javascript
jquery中表单 多选框的一种巧妙写法
2015/09/06 Javascript
jquery判断复选框是否被选中的方法
2015/10/16 Javascript
纯JS代码实现一键分享功能
2016/04/20 Javascript
jquery显示隐藏元素的实现代码
2016/05/19 Javascript
javascript实现将数字转成千分位的方法小结【5种方式】
2016/12/11 Javascript
jQuery UI制作选项卡(tabs)
2016/12/13 Javascript
React-router v4 路由配置方法小结
2017/08/08 Javascript
react.js使用webpack搭配环境的入门教程
2017/08/14 Javascript
seajs模块压缩问题与解决方法实例分析
2017/10/10 Javascript
node中的密码安全(加密)
2018/09/17 Javascript
优雅地使用loading(推荐)
2019/04/20 Javascript
vue项目开启Gzip压缩和性能优化操作
2020/10/26 Javascript
[02:14]DOTA2英雄基础教程 修补匠
2013/12/23 DOTA
[06:07]DOTA2-DPC中国联赛3月5日Recap集锦
2021/03/11 DOTA
浅析python继承与多重继承
2018/09/13 Python
Python基于内置库pytesseract实现图片验证码识别功能
2020/02/24 Python
SmartBuyGlasses台湾:名牌眼镜,名牌太阳眼镜及隐形眼镜
2017/01/04 全球购物
奢华时尚的独特视角:La Garçonne
2018/06/07 全球购物
什么是方法的重载
2013/06/24 面试题
工作失职检讨书
2015/01/26 职场文书
幼儿园春季开学通知
2015/07/16 职场文书
纯CSS实现酷炫的霓虹灯效果
2021/04/13 HTML / CSS
Spring Boot 实现 WebSocket
2022/04/30 Java/Android
使用JS前端技术实现静态图片局部流动效果
2022/08/05 Javascript