共计 3278 个字符,预计需要花费 9 分钟才能阅读完成。
这是 Hanjst/ 汉吉斯特 发布以来的首个主要升级更新版本。这次的主要升级更新的内容包括移除 HTML Comments 注释行, 优化在 Hanjst include 模板文件时的 JavaScript 运行时环境。
Hanjst 在设计和工程实现时,预留了模板文件嵌套功能,由于在过往的 Web 软件研发过程中,对模板文件的套用场景非常熟悉,所以对这一需求也感触较多,于是在最早的 Hanjst 初始发布中就预制了模板嵌套功能。
Hanst 的模板嵌套采用了与 -Smarty 等模板语言类似的语法表达:
{include file=”$anEmbedTplFile”}
其中“anEmbedTplFile”经由服务器端读取并生成想要的表达式语言字符串。然后经由 Hanjst JSON Data 传递给 Hanjst 模板解析引擎。
Hanjst 模板引擎在解析 include 指令时,将对应的模板表达式汇入当前的页面的当前位置,从而实现对模板的嵌套。这个过程很流畅,逻辑也与服务器端模板语言一致。所不同于服务器端模板语言的是,当我们在 anEbedTplFile 里声明 JavaScript 变量及对象时,在父模板的 JavaScript 运行时环境中可能找不到该变量或对象。设若,anEmbedTplFile 中包括如下内容:
<p>This is in an Embed Tpl File</p> <script> functionembedJSFunc(varA){console.log((new Date())+”: call embedJSFunc withvarA:”+varA); } embedJSFunc(“I’m in embed tpl file!”); </script><p>EOF.</p>
当我们将该模板以嵌套的形式进行嵌入一个 index.html 中时:
<p>This is in an Index Tpl File.</p> {include file=”anEmbedTplFIle”}<p>EOF.</p>
Hanjst 在解析 index 时,会将 anEmbedTplFile 的内容合并到 index 中,同时通过 _appendScript 私有方法将 embedJSFunc 注册到 index 中,同时在 index 的运行时环境执行 embedJSFunc 函数。
这次的主要升级改进就是这一流程中的细节进一步的优化提升。
1)资源冲突问题,当 index 中也有 embedJSFunc 同名函数时,会报错。该如何处理报错信息?可行方法是,避免在 index 和 embed 文件中同时命名相同的函数或变量、对象,以免引起冲突导致出错。即便有开发者意外地在 index 和 embed 中同样地声明了相同的函数后者变量,Hanjst 也做了异常处理,以便整体流程能够顺利执行并完成页面渲染。
2)如果在 embed 中有 <script src=””></script>, 需要考虑引入其脚步资源。该怎样引入较好? 路径中的变量如何处理?Hanjst 此次改进增加了对 embed 的引入资源的支持,对资源路径中的变量进行解析,如在 anEmbedTplFile 中:
<script async src=”{$tplDir}/js/adsbygoogle.js”></script>
则 Hanjst 在将 该 js 引入到 index 时,同时解析出 {$tplDir} 所指向的真实路径。
然而,如果是在 index 中模板中的路径,则需要在服务器端进行相应的替换,因为此时 Hanjst 尚未接管对页面的渲染,尤其是不能使用带有变量的路径来引入 Hanjst.js 文件。
在服务器端对资源路径进行替换的通常做法大致过程是:A)模板开发人员,前端设计制作人员通常会使用相对资源引用路径在交付设计稿时,B)工程技术人员拿到带有相对资源引用路径时,会分割出共用头文件和尾文件,其他嵌套文件块,然后藉由模板引擎的 {include} 指令,重新分割、组装模板,C)工程技术人员将其中的相对引用路径替换为带有模板绝对路径的输出内容文本。相应的替换逻辑可以在 -GWA2 Java 版中见到。
//- replaces in GWA2Java
String[] repTags = new String[]{“images”,“css”,“js”,“pics”};
for(int ti=0; ti<repTags.length; ti++){
outx.append(“ti:[“+ti+”] reptag:[“+repTags[ti]+”]”);
tplcont = tplcont.replaceAll(“\””+repTags[ti]+”/”,“\””+viewdir +”/”+repTags[ti]+”/”);
}
3)执行时序问题,如果是执行语句,绑定了相应的 DOM 对象,而该对象在 Hanjst 当前环境中还未就绪,会报错. 该如何处理?onComplete? window.setTimeout?
当 Hanjst 将 embed 的模板内容中的脚本注入到 index 的运行时环境时,在解决了资源冲突问题之后,需要考虑执行时序,尤其是当待执行的代码需要依靠 Hanjst 后续输出的 DOM 对象时,需要延后执行。
延后执行有两种思路,依靠 JavaScript 的事件驱动机制 event-driven, 将待执行的动作绑定到 Hanjst 的渲染方法 _renderTemplate 之后,也即 Hanjst._renderTemplate.onComplete。第二种思路是,依靠 JavaScript 的匿名函数 Anonymous Function 机制,既然是延后执行,则可以将待执行的命令封装到一个匿名函数,然后设定一个 window.setTimeout 过一段时间(预计 Hanjst 渲染完成 DOM)之后再触发该动作。
目前,Hanjst 采用的是后一种解决执行时序问题,也即当 embed 的动作要执行时,先以一个匿名函数封装并生成一个 window.setTimeout。
执行时序问题,不单是 embed 有,有时候 index 内的执行函数也有问题,一个常见的场景是,如果某些 JavaScript 调用是发生在 Hanjst 引用之前,则会被调用两次(重复!)。解决这个问题的思路就是将 待执行的命令放在 Hanjst 引用之后,或者在命令设计时,允许重复执行而不影响预计效果。
这个设计及实现可以在 Hanjst 的 Demo 页面看到。
Web 模板语言及其解析引擎深入下去才发现,复杂而庞大,希望这些细节的探究及描述可以帮助更多的开发人员理解其内幕,仅一个 {include} 指令的编译就涉及那么多方面,一些以玩票心态用几十行代码实现一个“轮子”的想法可以考虑暂停下来了。
Hanjst 解析引擎目前已经超过 600 行代码,包括很多类似这样的算一行的统计:
var asyncScriptArr = []; var isAsync = false; var srcPos = -1; var endTagPos = -1; //- four lines
matchStr = match[0]; exprStr = match[1]; //- two lines
if(exprStr.indexOf(‘=’) > -1){containsEqual = true;} //- three lines
毫无疑问,随着 Hanjst 部署的实例场景越多、兼容的异常越多、引入的模块越多和功能日趋强大,其代码会越来越长。
—-
Hanjst/ 汉吉斯特 是一种基于 JavaScript 的模板语言及解析引擎,她运行在客户端 / 服务器端。Hanjst 能够表述逻辑控制,能够实现与服务器端模块语言相同的功能。
Hanjst/ 汉吉斯特的主要特征 / 功能包括:Hanjst 当完全在客户端解析时,节省服务器端计算资源;
Hanjst 模板语言独立,不与服务器端资源做任何绑定;
纯粹的 MVC,层间数据用 JSON 格式传递;
常见模板语言功能全支持,附带复杂而强大的 JavaScript 编程能力;
无学习成本,直接使用 JavaScript 书写模板语言;
….Hanjst 新增部署实例参考: -Ufqi-Ztb , https://ztb.ufqi.com/?mod=ztb , 全国招投标信息中心 .
-R/s2SS