Vaadin 的 AbstractJavascriptComponent 的部分状态更改
Partial state changes for Vaadin's AbstractJavascriptComponent
我正在实现一个基于 JavaScript 的 Vaadin 组件,该组件需要显示和更新相对较大的数据集。我通过扩展 AbstractJavaScriptComponent
.
来做到这一点
我试图尽可能保持 JS 端 "dumb",使用 RPC 将用户交互委托给服务器,并更新共享状态。然后使用新状态调用 JS 连接器包装器的 onStateChange
函数,这会导致 DOM 相应地更新。
我有两个问题:
- 我不想在每次更新一小部分时都传输整个数据集。
- 我也不想每次都完全重建 UI。
我可以解决第二个问题,方法是保持以前的状态并比较它的各个部分以找出发生了什么变化,然后只进行必要的 DOM 更改。
但这还剩下第一个问题。
我是否必须停止使用 Vaadin 的共享状态机制,而只使用 RPC 来传达对状态的更改?
更新:
我一直在做一些测试,显然 Vaadin 的共享状态机制在效率方面很糟糕:
每当组件调用 getState()
以更新状态对象中的某些 属性(甚至不更新任何内容)时, 整个 状态对象被转移。据我所知,避免这种情况的唯一方法是不使用共享状态机制,而是使用 RPC 调用将特定状态更改传达给客户端。
RPC 方法存在一些需要解决的问题,例如:如果您在单个 request/response 周期内多次更改值,您不想进行 RPC 调用多次。相反,您只想发送最后一个值,就像共享状态机制只在响应中发送最终状态一样。您可以为要单独发送的状态的每个部分保留脏标志(或者只保留先前状态的副本并进行比较),但是您需要以某种方式在请求处理结束时触发 RPC 调用。如何做到这一点?
欢迎提出任何想法!
更新 2:
Vaadin 8 修复了根本问题:它仅发送已更改的状态属性。此外,当仅进行 RPC 调用(并且不更改任何状态)时,它不再在 JS 连接器上调用 onStateChange()
。
Vaadin 的共享状态完全按照您想要的开箱即用方式工作:当组件第一次添加到 DOM 时,整个共享状态从服务器传输到客户端,因此可以显示组件。之后,仅传输更改。例如,通过调用 component.setCaption("new caption")
更改可见组件的标题,Vaadin 仅将新的标题文本传输到客户端,"merges" 将其传输到组件的 client-side 共享状态实例。
OP 指出共享状态同步对于基于 AbstractJavaScriptComponent
的组件效率低下是正确的。每当连接器被标记为脏时,整个状态对象被序列化并可供 Javascript 连接器的 onStateChange
方法使用。其他非 javascript 组件通过仅发送更改来更智能地处理状态更新。代码中发生这种情况的确切位置是 com.vaadin.server.LegacyCommunicationManager.java
中的第 97 行
boolean supportsDiffState = !JavaScriptConnectorState.class
.isAssignableFrom(stateType);
我不确定为什么基于 AbstractJavaScriptComponent
的组件的状态更新处理方式不同。也许是为了简化 javascript 连接器并消除对 assemble 来自增量的完整状态对象的需要。如果能在未来的版本中解决这个问题就太好了。
正如您所建议的,您可以完全放弃 JavaScriptComponentState
并依靠服务器-> 客户端 RPC 进行更新。在您的服务器端组件中保留脏标志或通过您想要的任何机制比较旧状态和新状态。
要合并更改并为每个更改仅发送一个 RPC 调用,您可以在服务器端组件中覆盖 beforeClientResponse(boolean initial)
。这是在向客户端发送响应之前调用的,这是您添加一组 RPC 调用以更新客户端组件的机会。
或者,您可以重写 encodeState
,您可以自由地向客户端发送您喜欢的任何内容 JSON。您可以选择向 super.encodeSate
返回的基础 JSON 对象添加更改列表。您的 javascript 连接器可以在其 onStateChange
方法中进行适当的解释。
编辑添加:在服务器端组件中调用 getState()
会将连接器标记为脏。如果您想获取状态而不将其标记为脏,请改用 getState(false)
。
在我们对此进行讨论之后,我创建了 AbstractJavaScriptComponent 的直接替代品,它传输状态增量并包含一些额外的增强功能。它处于非常早期的阶段,但应该很有用。
https://github.com/emuanalytics/vaadin-enhancedjavascript
解决方案看似简单:基本上通过绕过 com.vaadin.server.LegacyCommunicationManager.java
中的这行代码重新启用状态差计算:
boolean supportsDiffState = !JavaScriptConnectorState.class
.isAssignableFrom(stateType);
解决方案的实施很复杂,因为 Vaadin 类 不容易扩展,所以我不得不复制并重新实施 6 类.
我正在实现一个基于 JavaScript 的 Vaadin 组件,该组件需要显示和更新相对较大的数据集。我通过扩展 AbstractJavaScriptComponent
.
我试图尽可能保持 JS 端 "dumb",使用 RPC 将用户交互委托给服务器,并更新共享状态。然后使用新状态调用 JS 连接器包装器的 onStateChange
函数,这会导致 DOM 相应地更新。
我有两个问题:
- 我不想在每次更新一小部分时都传输整个数据集。
- 我也不想每次都完全重建 UI。
我可以解决第二个问题,方法是保持以前的状态并比较它的各个部分以找出发生了什么变化,然后只进行必要的 DOM 更改。 但这还剩下第一个问题。
我是否必须停止使用 Vaadin 的共享状态机制,而只使用 RPC 来传达对状态的更改?
更新: 我一直在做一些测试,显然 Vaadin 的共享状态机制在效率方面很糟糕:
每当组件调用 getState()
以更新状态对象中的某些 属性(甚至不更新任何内容)时, 整个 状态对象被转移。据我所知,避免这种情况的唯一方法是不使用共享状态机制,而是使用 RPC 调用将特定状态更改传达给客户端。
RPC 方法存在一些需要解决的问题,例如:如果您在单个 request/response 周期内多次更改值,您不想进行 RPC 调用多次。相反,您只想发送最后一个值,就像共享状态机制只在响应中发送最终状态一样。您可以为要单独发送的状态的每个部分保留脏标志(或者只保留先前状态的副本并进行比较),但是您需要以某种方式在请求处理结束时触发 RPC 调用。如何做到这一点?
欢迎提出任何想法!
更新 2:
Vaadin 8 修复了根本问题:它仅发送已更改的状态属性。此外,当仅进行 RPC 调用(并且不更改任何状态)时,它不再在 JS 连接器上调用 onStateChange()
。
Vaadin 的共享状态完全按照您想要的开箱即用方式工作:当组件第一次添加到 DOM 时,整个共享状态从服务器传输到客户端,因此可以显示组件。之后,仅传输更改。例如,通过调用 component.setCaption("new caption")
更改可见组件的标题,Vaadin 仅将新的标题文本传输到客户端,"merges" 将其传输到组件的 client-side 共享状态实例。
OP 指出共享状态同步对于基于 AbstractJavaScriptComponent
的组件效率低下是正确的。每当连接器被标记为脏时,整个状态对象被序列化并可供 Javascript 连接器的 onStateChange
方法使用。其他非 javascript 组件通过仅发送更改来更智能地处理状态更新。代码中发生这种情况的确切位置是 com.vaadin.server.LegacyCommunicationManager.java
boolean supportsDiffState = !JavaScriptConnectorState.class
.isAssignableFrom(stateType);
我不确定为什么基于 AbstractJavaScriptComponent
的组件的状态更新处理方式不同。也许是为了简化 javascript 连接器并消除对 assemble 来自增量的完整状态对象的需要。如果能在未来的版本中解决这个问题就太好了。
正如您所建议的,您可以完全放弃 JavaScriptComponentState
并依靠服务器-> 客户端 RPC 进行更新。在您的服务器端组件中保留脏标志或通过您想要的任何机制比较旧状态和新状态。
要合并更改并为每个更改仅发送一个 RPC 调用,您可以在服务器端组件中覆盖 beforeClientResponse(boolean initial)
。这是在向客户端发送响应之前调用的,这是您添加一组 RPC 调用以更新客户端组件的机会。
或者,您可以重写 encodeState
,您可以自由地向客户端发送您喜欢的任何内容 JSON。您可以选择向 super.encodeSate
返回的基础 JSON 对象添加更改列表。您的 javascript 连接器可以在其 onStateChange
方法中进行适当的解释。
编辑添加:在服务器端组件中调用 getState()
会将连接器标记为脏。如果您想获取状态而不将其标记为脏,请改用 getState(false)
。
在我们对此进行讨论之后,我创建了 AbstractJavaScriptComponent 的直接替代品,它传输状态增量并包含一些额外的增强功能。它处于非常早期的阶段,但应该很有用。
https://github.com/emuanalytics/vaadin-enhancedjavascript
解决方案看似简单:基本上通过绕过 com.vaadin.server.LegacyCommunicationManager.java
中的这行代码重新启用状态差计算:
boolean supportsDiffState = !JavaScriptConnectorState.class
.isAssignableFrom(stateType);
解决方案的实施很复杂,因为 Vaadin 类 不容易扩展,所以我不得不复制并重新实施 6 类.