我们是刚刚成立的, 一家传统的软件开发公司(只有几个人的小公司), 主营业务就是传统行业软件项目的外包.
由于这种项目需要的技术不深, 但是对开发效率有很高的要求, 所以我们在慢慢的摸索一下快速开发模式.
同时也愿意把其中不杂乱的部分,分享出来.
这一系列的文章主要是针对GXTemplate, 一个模板套用类库 (谈不上框架)
这并不是一个 "如何使用" 的教程 , 而是一个 "如何创造" 这种类库的教程.
特点是, 我们会把整个创造过程, 由v0.1的原始版本开始, 一步一步如何加代码的, 逐渐展示给大家观看.
如果大家是初学者, 想在JavaScript方面有进一步的提高, 那么可以持续关注一下这系列的教程.
首先最初的版本v0.1是这个样子的: (v1.0已完成. 但教程要分开多篇编写)
下载地址 https://github.com/zhgangxuan/GXTemplate/blob/master/_oldfiles/version1/template01.html
源代码 (默认折叠,请展开):
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>text node</title> <script src="data.js"></script> </head> <body> <h1>{#myitem.name}</h1> <p> Address: {#myitem.address} - Zip: {#myitem.zipcode} - Phone: {#myitem.phone} </p> </body> <script> var re_template_textbinding = /{#([^}]+)}/g; function ProcessEval($__exp__) { return eval($__exp__); } function ProcessTextNode(node, str) { var newstr = str.replace(re_template_textbinding, function (exp, g1, index, full) { return ProcessEval(g1); }); if (newstr != str) node.nodeValue = newstr; } function TemplateExecute(node) { if (node.nodeType == 3) { var str = node.nodeValue; if (str.indexOf(‘{#‘) != -1) ProcessTextNode(node, str); return; } if (node.nodeType != 1) return; switch (node.nodeName) { case "SCRIPT": case "STYLE": return; } var cns = node.childNodes; for (var ni = cns.length; ni > 0; ni--) TemplateExecute(cns.item(cns.length - ni)); } TemplateExecute(document.body); </script> </html>
目标 :
让文本内容支持 {#脚本返回值} 这个语法
这样我们就可以避免在页面中放太多带id的span, 和在分离的JS代码中去设置这个span的innerText了.
在快速开发的角度上, 代码越少, 越不分离, 就越好
实现过程:
由于在脚本执行的时候, HTML代码已经转化为具体的DOM对象了. 所以我们的策略是直接对DOM树进行递归.
在例子里, 从document.body开始搜索, 把整个文档都搜个遍. (但实际使用时建议从一个小范围内进行处理)
TemplateExecute(document.body);
TemplateExecute里的递归方式, 是倒数方式. 先从倒数第n项开始, 然后是第n-1项, 然后是第n-2,...一直到倒数第一项.
var cns = node.childNodes; for (var ni = cns.length; ni > 0; ni--) TemplateExecute(cns.item(cns.length - ni));
在执行的过程中, 如果遇到一些不需操作textnode的Element, 也要跳过
switch (node.nodeName) { case "SCRIPT": case "STYLE": return; }
在递归的过程中, 如果找到textnode, .nodeName=="#text"或 .nodeType==3, 那么就执行ProcessTextNode函数
if (node.nodeType == 3) { var str = node.nodeValue; if (str.indexOf(‘{#‘) != -1) ProcessTextNode(node, str); return; }
str.indexOf(‘{#‘)是一个快速跳过没有指令的内容的过程, 实测能提高性能.
在ProcessTextNode内, 使用正则表达式 var re_template_textbinding = /{#([^}]+)}/g; 去替换掉所有 {# ... } 表达式
如果有满足的表达式并且执行脚本后的文本和原来不同, 那么使用node.nodeValue = newstr就可以改写textnode要显示的文本
function ProcessTextNode(node, str) { var newstr = str.replace(re_template_textbinding, function (exp, g1, index, full) { return ProcessEval(g1); }); if (newstr != str) node.nodeValue = newstr; }
而ProcessEval目前很简单, 只是使用JavaScript的内置函数eval执行一下. 以后会有更好的方式.
function ProcessEval($__exp__) { return eval($__exp__); }
具体的数据myitem是定义在data.js里, 被后续的例子共同引用:
var myitem = { name: "mobile shop", address: "No.1234 Fenghuang Road", zipcode: "519000", phone: "0756-0000000" };
这个例子运行了之后的显示结果是这样的
<h1>mobile shop</h1> <p>Address: No.1234 Fenghuang Road - Zip: 519000 - Phone: 0756-0000000</p>
我们想逐渐建立起一套简单易用的, 适合传统项目的ASP.NET+C#+JavaScript的开发方案.
网上各种开源框架虽然厉害, 但是对于新手来说却很难很花时间.
因为对于很多中小软件公司来说, 他们经常会招到一些入门开发者. 太复杂的框架不适合人员调动或二次开发.
所以我们不惜花时间去重做这些东西. 希望能帮助到国内的初级开发者, 为国内的信息化建设贡献点绵力.
实际上整个GXTemplate的v1.0版都已经上传到github的了. 接下来我们的业余时间主要是谢谢例子, 写写教程.
对于不懂得使用github的朋友, 可以直接下载源代码 https://github.com/zhgangxuan/GXTemplate/archive/master.zip