Limited Razor,在JavaScript中使用Razor式的模板
2010-08-01 20:46 Nana's Lich 阅读(3156) 评论(7) 编辑 收藏 举报最近,微软公布了新的WebMatrix便捷Web开发环境——有些人可能还记得许多年以前也有一个WebMatrix,但其实这次的新WebMatrix除了因出于同样的目的被创造出来而沿用了WebMatrix以外,和多年前的那个WebMatrix完全是两回事。
新的WebMatrix同时还为我们带来了IIS Express、SQL Server CE 4和Razor视图引擎。
Razor视图引擎和WebForms不同,是ASP.NET的另一种形式,Razor的特点完全可以用一个词来概括:简洁!
关于Razor视图引擎,以及IIS Express、SQL Server CE 4、WebMatrix的信息,可以在Scott Guthrie's Blog找到,这里就不多讲了。
在最近的一个项目中,我试图找到一个可以让网页很容易在浏览器端模板化、又可以渐进展现、不像XML+XSLT那样需要等待全文件加载之后才能显示的办法。
在这个过程中,我受到了Razor视图引擎的启发——虽然浏览器端的类Razor实现可能不是解决我的问题的最好的办法,但我还是先行动起来了。
依照着Razor引擎的思想,经过4个小时的编写和调整,我实现出了一个类Razor的模板处理器。由于其功能相比于正牌的Razor还比较有限,所以我暂时叫它Limited Razor。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | function lyRazor(s) { var m, rx = new RegExp( '@@|@' , 'g' ); var dw = 'document.write("' ; var r = dw, li = 0, i, l; while (m = rx.exec(s)) { r += jsEncode(s.substring(li, i = m.index)); switch (l = m[0].length) { case 1: var exp, sf, find = s.charAt(i + 1); if (find == '(' ) { r += '"+' ; find = '\\)' ; sf = '+"' ; } else if (find == '{' ) { // 代码块 r += '");' ; i++; // 从下一个字符开始寻找 find = '\\}' ; sf = ';' + dw; } else { find = '[\\s<;]' ; // 寻找空白字符或者尖括号 sf = null ; } li = i + 1; // 跳过@ var rxSpc = new RegExp(find, 'g' ); rxSpc.lastIndex = li; while (m = rxSpc.exec(s)) { try { exp = s.substring(li, m.index); m = m[0]; if ( ')' == m) exp += ')' ; // 附加右括号 else if ( ';' == m) l++; new Function(exp + ';' ); // 测试语法 l += exp.length; switch (m) { case '<' : // 不包括 break ; case ';' : case ')' : // 已经附加到exp,但不确定是否有多余的空白字符 var m2 = /^\r\n|^\s/.exec(s.substr(l + i, 2)); // 连续的\r\n被当作“一个”空白字符来消除 if (m2) l += m2[0].length; break ; case '}' : // 代码块结束 if ( '}\r\n' == s.substr(l + i, 3)) // 连续的\r\n被当作“一个”空白字符来消除 l += 2; l++; break ; default : // 从空字符之后开始处理 if ( '\r\n' == s.substr(l + i, 2)) // 连续的\r\n被当作“一个”空白字符来消除 l++; l++; break ; } break ; } catch (ex) { } } if (!m) // 无法匹配为js语法 throw 'bad syntax!' ; if (sf) // 块或括号 r += exp + sf; else if ( ';' == m) { // 语句 r += '");' + exp + ';' + dw; } else // 表达式 r += '"+(' + exp + ')+"' ; break ; case 2: r += '@' ; // @自转义 break ; default : break ; } li = i + l; } return r + jsEncode(s.substr(li)) + '");' ; } |
实现这个东西的核心思想就是寻找@,然后根据@之后的内容来寻找特定的边界字符,再进行语法测试。
进行语法测试的时候我用了一个比较懒但很有效的办法:把代码作为body参数直接传给Function构造器。这个办法也可以用在其它的需要对用户输入、或者程序产生的JavaScript代码进行语法检测,而不用担心框架在不正确的时间执行这些代码。
在这个实现中,我用了一个名为jsEncode的自定义函数,用来对那些不能原样写在字符串表达式中的特殊字符进行转义,它的实现原理也挺简单,所以有兴趣的同学请自己想想应该怎么实现。
这个Limited Razor处理器现在支持这样的用法:
1 2 3 4 5 6 7 | 变量/属性:@location.href< br > 字面量:@1< br > 计算:@1/7< br > 括号:@(1 + 2)< br > 函数:@parseInt("7f", 16)< br > 语句:@document.write('test');< br > 代码块:@{ document.write("Hello, Razor!"); }< br > |
目前这个Limited Razor还不支持@if、@while、@for等在@之后直接使用流控制语句的用法,也没有Helper之类的支持。也许以后我会把这些都加上。
具体使用的示范:
1 2 3 4 5 6 | <script type="text/l-razor" id="tmpl"> <ul> <li>语句调用:@document.write("Hello, Razor!"); </li><li>属性调用:@location.href </li></ul> </script> |
1 2 3 4 | < script type="text/javascript"> var code = lyRazor(document.getElementById("tmpl").text); eval(code); </ script > |
好了,现在有一个简单的思考题:
很多AJAX(RIA)的页面是在页面文档已经加载完毕之后才对页面内容进行改变的,这个时候是不能使用document.write方法来输出HTML的。如果遇到了这样的情况,这个Limited Razor应该怎么改呢?
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器