2

我目前正在使用Vaadin Flow 版本 14 ( https://github.com/vaadin/platform/releases/tag/14.0.0 )
我运行 Java 版本 1.8.0_231,64 位。

我只是希望能够在用户执行以下任一操作时检测到(在 java 中!):

  • 关闭当前浏览器选项卡(或其浏览器)
  • 在他们的键盘上点击 F5,或者在他们的浏览器中按下刷新按钮
  • 单击链接(或其他任何内容),将它们从我的网站重定向

我已经尝试了很多不同的方法来检测这一点。到目前为止,我唯一能检测到的是当前 VaadinSession 到期时(我可以通过做来更改VaadinSession.getCurrent().getSession().setMaxInactiveInterval(15))。这使得每个会话在 15 秒后过期(在我的例子中,15 只是一个测试数字)。



使用Vaadin 8,我相信您可以做到这一点,并且开箱即用:

        JavaScript.getCurrent().execute(
            "function closeListener() { catchClose(); } " +
                    "window.addEventListener('beforeunload', closeListener); " +
                    "window.addEventListener('unload', closeListener);"
        );
        JavaScript.getCurrent().addFunction("catchClose", arguments ->
        {
            System.out.println("user has quit :o");
        });

我不能使用这个,因为我使用的是Vaadin 14,而不是8


我尝试将其添加到我的 View 类中:

    @Override
    protected void onDetach(DetachEvent detachEvent)
    {
        System.out.println("onDetach " + detachEvent);
    }

它仅在会话到期时打印此消息(在我的情况下为 15 秒)。即使我不关闭页面。


我已经尝试实现BeforeLeaveObserver / BeforeLeaveListener并覆盖它:

    @Override
    public void beforeLeave(BeforeLeaveEvent beforeLeaveEvent)
    {
        System.out.println("beforeLeave");
    }

这从不打印。


我也尝试了所有这些,但他们没有做我需要的:

        VaadinResponse.getCurrent().getService().addUIInitListener(e ->
        {
            System.out.println("1 addUIInitListener : " + e);
            e.getUI().addBeforeLeaveListener(e2 ->
            {
                System.out.println("1.1 addBeforeLeaveListener : " + e2);
            });
            e.getUI().addDetachListener(e2 ->
            {
                System.out.println("1.2 addDetachListener : " + e2);
            });
        });
        VaadinResponse.getCurrent().getService().addServiceDestroyListener(e ->
        {
            System.out.println("2 addServiceDestroyListener : " + e);
        });
        VaadinResponse.getCurrent().getService().addSessionDestroyListener(e ->
        {
            System.out.println("3 addSessionDestroyListener : " + e);
        });

1 addUIInitListener当用户加载页面时打印。
1.1 addBeforeLeaveListener从不打印。
2 addServiceDestroyListener从不打印。服务与会话不同。有道理。
1.23 addSessionDestroyListener在一段时间后打印。需要15-30秒。


有没有办法基本上立即检测到这一点?:/

4

2 回答 2

1

检测关闭的 JavaScript 方法仍然有效。

语法只是稍微改变了一点

UI.getCurrent().getPage().executeJs("function closeListener() { $0.$server.windowClosed(); } " +
        "window.addEventListener('beforeunload', closeListener); " +
        "window.addEventListener('unload', closeListener);",getElement());


@ClientCallable
public void windowClosed() {
    System.out.println("Window closed");
}

虽然这不是 100% 防弹的方式,因为我认为并非所有浏览器都以相同的方式工作。例如,上面的代码在 Chrome 中触发了两次,只要听就beforeunload足够了。

这允许将 JavaScript 简化为

    UI.getCurrent().getPage().executeJs(
"window.addEventListener('beforeunload', () => $0.$server.windowClosed()); ",getElement());

BeforeLeaveObserver与导航一起使用。因此,如果您使用RouterLink或调用ui.navigate("route"),则会调度BeforeLeaveEvent,但不会在调用page.setLocation().

浏览器会因为崩溃而关闭,或者因为其他原因而丢失,那beforeunload自然不会发生。最后的手段是基于心跳的检测机制。即在三个丢失的心跳后 Vaadin 服务器将得出结论,浏览器丢失。这自然有延迟,这根本无法避免。

于 2020-02-06T06:21:48.460 回答
1

根据您需要支持的浏览器,您可能还想查看Beacon API,它被所有现代浏览器支持,但不支持 IE11。

Beacon API 具有非阻塞的优点。使用卸载侦听器,客户端在关闭选项卡之前等待响应,这可能会给用户带来不好的体验。

尽管如此,正如 Tatu 所说,如果浏览器崩溃或用户失去互联网连接,您所能做的就是等待会话超时。

于 2020-02-06T06:34:55.657 回答