开发过程必备除错基本知识 - 处理 HTTP 要求的内部运作

ZK 处理 zul 要求跟 ajax 要求是由不同的两个 servlet 处理,过程不太相同。

处理zul 要求

DHtmlLayoutServlet负责处理 zul 要求,你可以打开 developer tool 来观察 zul 要求的回传内容:

https://ithelp.ithome.com.tw/upload/images/20211008/20050621I5JhZOGSXZ.jpg

  • wpd 档其实就是 JavaScript 档。zk 会把相同 package 的 widget 打包成一个 wpd 档案,这样可以一个 request 下载。你看到的 3 个 wpd 是基本、必要的档案,不管你使用哪些元件都会下载这3个 wpd
  • wcs 则包含所有元件的外观 CSS

如果页面上有用基本元件以外的元件,ZK 会根据页面用到的 widget 再动态下载对应的 javascript。例如页面用到 <borderlayout>, <script> ,因此除了基本的3 个 wpd 外,会额外下载 zul.layout.wpd 跟 zul.utl.wpd。

https://ithelp.ithome.com.tw/upload/images/20211008/200506216efgUMCA2p.jpg

处理 AJAX 要求 (AU request)

当你用浏览器访问 zul 页面後,接下来跟元件互动的过程都只会发出非同步的 ajax 请求,因此你会发现页面的 URL 并不会改变。

什麽是 AU

AU 代表 Asynchronous Update,是指 ZK 从浏览器发出 AJAX 并局部更新页面的过程。

AU 处理流程

我用一个简单的点击按钮触发倾听器的动作来说明 au request 的处理流程。

https://ithelp.ithome.com.tw/upload/images/20211008/20050621V13FxXO4mX.png

  1. 使用者点击按钮,触发 DOM 事件
  2. ZK JavaScript widget 发出事件通知 client engine
  3. client engine 将事件透过 AJAX 传递到服务器
  4. 服务器将 AJAX request 转换成 ZK Event 发给事件目标 ZK 元件,本例中是 Button
  5. 元件处理完事件,发给内建的事件伫列
  6. 系统呼叫该事件的倾听器 (event listener),通常是在控制器内
  7. 执行事件倾听器(通常是实作应用程序逻辑),可能包含存取 ZK 元件、存取资料库、或是再发出事件
  8. 当所有在伫列中的事件都被处理完之後,元件被变更的部分都会被收集起来并转换成对 ZK JavaScript widget 的命令
  9. 将命令透过 AJAX response 传回前端,由 client engine 去通知对应的 widget
  10. ZK widget 根据更新的资料绘制、更新其所属的 DOM elements,就达到更改画面的效果

AU request

当事件在浏览器被触发,ZK 元件会发出 AU request,透过开发者工具 (developer tool) 可得知事件的细节。你可按 F12 在 Chrome 或 Firefox 中都能打开 developer tool。以下是 Chrome的画面,请观察 Form Data,其中包含以下栏位:

https://ithelp.ithome.com.tw/upload/images/20211008/20050621JygH3Pza9R.png

  • dtid, Desktop id
  • cmd, 事件名称, 例如 onClick
  • uuid, 发生该事件的 DOM element id
  • data, 事件相关资料,每个事件会带不同的资料

因为有时候会有多个事件累积起来一次发送,因此 cmd 等 key 会後缀编号,代表事件的顺序,如 cmd_0, cmd_1,其对应的资料也会有同样的编号如 uuid_0, data_0

AU response

当事件倾听器执行完後,就会回传给 client engine 的命令,你可打开 developer tool 来观察回应内容:

https://ithelp.ithome.com.tw/upload/images/20211008/20050621d0Khy6Eowp.png

从 AU response 内容就可以回推我们在倾听器中呼叫了哪些元件上的 method,因此这些资讯也可以用来除错。这里我们不用细究每个命令後面带的一大堆参数,只要能看出几个关键字即可得知大概,下面介绍一些常见的。

设定元件属性: setAttr

呼叫任何 setter method 都会产生 setAttr 这个命令(代表 setAttribute ),例如下面就是呼叫 setVisible(false)

{"rs":[["setAttr",[{$u:'cMIYd'},"visible",false]]],"rid":1}

增加子元件: addChd

{"rs":[["addChd",["iB0Zk0",[
['zul.sel.Treeitem','iB0Zy1',{open:false,_loaded:true},{},[
['zul.sel.Treerow','iB0Zz1',{},{},[
['zul.sel.Treecell','iB0Z_2',{label:'< 2.0L(inc.)’},{},[]],
['zul.sel.Treecell','iB0Z02',{label:'3'},{},[]]]]]]],…,"rid":7}

删除元件: rm

{"rs":[["rm",["zY5Q10"]],["rm",["zY5Qz"]],["rm",["zY5Q60"]],["rm",["zY5Q80"]],["rm",["zY5Qa0"]],["rm"

重绘元件: outer

重绘元件会造成只存在於 client 端的状态会被服务器端的状态覆写,例如我展开一个 groupbox,但这个开启状态如果没有注册 onOpen 倾听器是不会送到服务器端,因此如果有任何原因促使重绘 groupbox 的话,就会造成 groupbox 变成关闭。

{"rs":[["outer",[{$u:'yZ6Q2'},[
['zul.tab.Tabbox','yZ6Q2',{width:'20%',prolog:'\n\t'},{},[
['zul.tab.Tabs','yZ6Q3',{},{},[
['zul.tab.Tab','yZ6Q8',{$onSelect:false,$onClose:true,label:'tab 1’,iconSclass:'z-icon-book',selected:true},{},[]],
['zul.tab.Tab','yZ6Qf',{$onSelect:false,$onClose:true,label:'tab 2’,iconSclass:'z-icon-book'},{},[]]]],
…],"rid":1}

<<:  中阶魔法 - Callback Function

>>:  23. 闲聊 x VFS 办理英签申请

过渡到特殊教育需求教学

要考虑的主要和最重要的事情是熟悉不同特殊教育需求的行话和定义。成功的教学要求教师了解每个学生及其个人...

Day 26 Ruby Symbol

在 Ruby 内有符号(Symbol)这个物件,他跟字串的用法蛮像的,但本质上则不一样。 究竟 Sy...

SQL Server 每日定期备份与定期删除旧有备份档

SQL Server 资料库备份是将存放在资料库里面的资料,转成单一档案保存,通常是副档名为 bak...

[DAY29] 用 Application Insights 来监视部署的模型

DAY29 用 Application Insights 来监视部署的模型 我们已经把大部份的 Az...

Day 57. 系列完结心得

嗨,我是Bear。 遵守在Day30时与自己的约定,於年前写完所有设计模式。 稍晚会将目录更新至Da...