曾经阅读过《只有20行JAVASCRIPT代码, 手把手教你写一个页面模版引擎》这篇文章, 对其中实现模版的想法实在膜拜, 于是有了这篇读后感, 谈谈自己对模版引擎的理解, 以及用自己的语言探讨他的实现方式, 加深理解

文章地址: http://krasimirtsonev.com/blog/article/Javascript-template-engine-in-just-20-line

前端编写过程中, 经常性的会出现需要在js中组合, 或生成html插入到页面中

类似:

  1. $.get('/data.json', function (json) {
  2. var dataList = json.data,
  3. index, length, html = '';
  4.  
  5. for (index = 0, length = dataList.length; index < length; index++) {
  6. html += '<div>' + dataList[index'].name + '</div>';
  7. }
  8.  
  9. document.getElementById('content').innerHTML = html;
  10. });

如果需要生成的html比较简单, 没有什么逻辑判断的时候, 并不觉得有什么问题, 但...

  1. $.get('/data.json', function (json) {
  2. var dataList = json.data;
  3. index, length, data, html = '';
  4.  
  5. for (index = 0, length = dataList.length; index < length; index++) {
  6. data = dataList[index];
  7.  
  8. html += '<div'
  9. if (data.isDelete) {
  10. html += ' class="red" ';
  11. }
  12. html += '>';
  13.  
  14. if (data.sex == 1) {
  15. html += '男';
  16. } else {
  17. html += '女';
  18. }
  19.  
  20. html += ' age: ' + (data.age - 1);
  21.  
  22. html += '</div>';
  23. }
  24. });

这种时候, 就有很多坑了, 经常性的标签没有正确闭合, 而且不输出html的情况下, 无法预测html的结构, 实在坑爹... 这时候, 模版引擎的优势就体现出来了

在《深入理解PHP》一书中, 提到, PHP的模版引擎, 就是把你发明的语言, 通过你实现的编译器, 翻译成PHP语言, 再给PHP执行.

同样的, JS模版引擎也是类似的

把你发明的语言, 通过你实现的编译器, 翻译成HTML, 再插入到DOM中, 就是JS模版引擎的工作的, 实际上, 模版引擎应当只负责翻译的工作, 是否插入到DOM中, 或者是怎么插入, 就由自己选择了

实现模版引擎, 关键以及重点其实就是在于 正则表达式 的运用, 通过正则表达式, 获取模版中的关键字, 通过各种方法去替换, 或循环, 或逻辑判断, 最后组合返回HTML

在文章中, 使用<% %>作为替换的标志, 类似与<?php ?>之间的代码会被php执行一样, 规定<%%>之间的代码, 才会被执行, 因此得出的正则

  1. var reg = /<%([^%>]+)%>/g;

通过该正则循环获取, 并替换

  1. while (match = reg.exec(html)) {
  2. //code
  3. }

为了添加逻辑判断, 在<%%>中, 如果出现if, else, case, break, switch等关键字, 就不输出, 直接当做js执行, 所以添加了另外一句正则

  1. var reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g;

对正则的编写是整个模版引擎的核心, 其余便是一些字符串替换以及如何巧妙得拼接字符串, 该文章采用了new Function的方式, 再使用一个数组r, 往数组r中push字符串, 再最后join输出, 实在是巧妙

最后的15行代码:

  1. var TemplateEngine = function(html, options) {
  2. var re = /<%([^%>]+)?%>/g, reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g, code = 'var r=[];\n', cursor = 0;
  3. var add = function(line, js) {
  4. js? (code += line.match(reExp) ? line + '\n': 'r.push('+ line + ');\n') :
  5. (code += line != '' && line.replace(/\s/g, "") != '' ? 'r.push("'+ line.replace(/"/g, '\\"') + '");\n': '');
  6. return add;
  7. }
  8. while(match = re.exec(html)) {
  9. add(html.slice(cursor, match.index))(match[1], true);
  10. cursor = match.index + match[0].length;
  11. }
  12. add(html.substr(cursor, html.length - cursor));
  13. code += 'return r.join("");';
  14. return new Function(code.replace(/[\r\t\n]/g, '')).apply(options);
  15. }

在实际使用过程中, 发现了一些小问题

如果在使用逻辑判断switch中, 模版有换行的时候, new Function中会插入了多个

  1. r.push("");
  2. switch(str) {
  3. r.push("");
  4. case 1:
  5. r.push("");
  6.  
  7. break:
  8. r.push("");
  9. }

所以 add 方法在 js == false的情况, 应当还要判断line是否为空字符串

  1. var add = function(line, js) {
  2. js? (code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n') :
  3. (code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : '');
  4. return add;
  5. }
  6.  
  7. //改为
  8.  
  9. var add = function(line, js) {
  10. js? (code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n') :
  11. (code += line != '' && line.replace(/\s/g, "") != "" ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : '');
  12. return add;
  13. }

最后结果

  1. var templateEngine = function(html, options) {
  2. var re = /<%([^%>]+)?%>/g,
  3. reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g,
  4. code = 'var r=[];\n',
  5. cursor = 0;
  6. var add = function(line, js) {
  7. js ? (code += line.match(reExp) ? line + '\n': 'r.push(' + line + ');\n') : (code += line != '' && line.replace(/\s/g, "") != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n': '');
  8. return add;
  9. }
  10. while (match = re.exec(html)) {
  11. add(html.slice(cursor, match.index))(match[1], true);
  12. cursor = match.index + match[0].length;
  13. }
  14. add(html.substr(cursor, html.length - cursor));
  15. code += 'return r.join("");';
  16.  
  17. return new Function(code.replace(/[\r\t\n]/g, '')).apply(options);
  18. }

实际使用时, 个人喜欢在html中插入一个type="text/tpl"的script作为模版标签

  1. <script type="text/tpl" id="tpl">
  2. <% for (var i = 0, length = this.length; i < length; i++) { %>
  3. <div <% if (this[i].isDeleted) { %>class="red"<% } %>>
  4. <% if (this[i].sex == 0) { %>

  5. <% } else { %>

  6. <% } %>
  7. </div>
  8. </script>

获取该script的innerHTML后, 通过模版引擎编译, 再插入到需要插入的DOM中

JS模版引擎[20行代码实现模版引擎读后感]的更多相关文章

  1. HTML5游戏实战(4): 20行代码实现FlappyBird

    这个系列很久没有更新了.几个月前有位读者调侃说,能不能一行代码做一个游戏呢.呵呵,接下来一段时间,我天天都在想这个问题,怎么能让GameBuilder+CanTK进一步简化游戏的开发呢.经过几个月的努 ...

  2. 20 行代码极速为 App 加上聊天功能

    现在很多 App 都需要集成 IM 功能,今天就为大家分享一下集成 IM 基本功能的步骤.本文内容以 JMessage 为例.极光 IM ( JMessage ) = 极光推送 ( JPush ) + ...

  3. Blazor组件自做九: 用20行代码实现文件上传,浏览目录功能 (3)

    接上篇 Blazor组件自做九: 用20行代码实现文件上传,浏览目录功能 (2) 7. 使用配置文件指定监听地址 打开 appsettings.json 文件,加入一行 "UseUrls&q ...

  4. HTML5游戏实战之20行代码实现打地鼠

    之前写过一篇打地鼠的博客70行的代码实现打地鼠游戏,细致思考过后,发现70行代码都有点多余了,应用tangide的控件特性,能够将代码量缩减到20行左右. 先show一下终于成果,点击试玩:打地鼠.或 ...

  5. 20 行代码:Serverless 架构下用 Python 轻松搞定图像分类和预测

    作者 | 江昱 前言 图像分类是人工智能领域的一个热门话题.通俗解释就是,根据各自在图像信息中所反映的不同特征,把不同类别的目标区分开来的图像处理方法. 它利用计算机对图像进行定量分析,把图像或图像中 ...

  6. 1. node.js环境搭建 第一行代码

    一.NodeJs简介 NodeJS官网上的介绍: Node.js is a platform built on  Chrome's JavaScript runtime  for easily bui ...

  7. Grid布局20行代码快速生成瀑布流

    网格布局 Grid 布局,好用又简单,至少比 Flex 要人性化一点,美中不足就是浏览器支持度差点. DOM结构 中间夹层为了后续拓展. CSS .grid { display: grid; grid ...

  8. javascript写贪吃蛇游戏(20行代码!)

    <!doctype html> <html> <body> <canvas id="can" width="400" ...

  9. 学会python可以上天!20行代码获取斗鱼平台房间数据,就是这么牛逼!

    Python(发音:英[?pa?θ?n],美[?pa?θɑ:n]),是一种面向对象.直译式电脑编程语言,也是一种功能强大的通用型语言,已经具有近二十年的发展历史,成熟且稳定.它包含了一组完善而且容易理 ...

随机推荐

  1. uvalive 3938 "Ray, Pass me the dishes!" 线段树 区间合并

    题意:求q次询问的静态区间连续最大和起始位置和终止位置 输出字典序最小的解. 思路:刘汝佳白书 每个节点维护三个值 pre, sub, suf 最大的前缀和, 连续和, 后缀和 然后这个题还要记录解的 ...

  2. git高级应用

    高级应用之一:   二分法查找错误版本: 当前日志 $ git log --oneline ccda9d2 added test1 dd518f7 test zu 88095f9 dasfdasf 3 ...

  3. Hbase集群无法关闭

    执行stop-hbase.sh关闭Hbase服务器,提示一直在等待,查阅了很多网上的资料找到了答案.因为hbase的主要信息存储在zookeeper集群中,zookeeper集群没有正常启动会导致hb ...

  4. poj 3984 迷宫问题【bfs+路径记录】

    迷宫问题 Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 10103   Accepted: 6005 Description ...

  5. java中通过反射获取方法并且调用(getMethod和invoke深入)实践

    为了支持业务的快速变更,往往采用可配置的方式,将业务逻辑的处理部分配置在数据库中或者XMl文件里.配置什么,如何配置才更灵活,That's a problem. 以数据库配置为例(xml相同),在数据 ...

  6. CALayer 的 position和anchorPoint属性

    在iOS 中,UIButton.UIImage等UIView 之所以能够显示在屏幕上,是因为其内部有一个图层(CALayer).通过UIView的layer 属性可以访问这个图层: @property ...

  7. zend studio-字体大小设置

    在使用zend studio的过程中为了方便我们编码,很多时候需要设置编码的字体的大小,设置步骤如下: 选择[Windows]-[preference]-[general]-[appearance]- ...

  8. [TypeScript] 1. Catching JavaScript Mistakes with TypeScript

    The TypeScript compiler is a powerful tool which catches mistakes even in vanilla JavaScript. Try it ...

  9. linux 修改文件时间

    1.ls -l *.sh 2.touch -d "10/13/2013" *.sh [我想把所以的.sh文件修改到三个月前(2013年10月13)的时间.]3.ls -l *.sh ...

  10. [转] 引用 Java自带的线程池ThreadPoolExecutor详细介绍说明和实例应用

    PS: Spring ThreadPoolTaskExecutor vs Java Executorservice cachedthreadpool 引用 [轰隆隆] 的 Java自带的线程池Thre ...